技术博客一键多发实战:基于 Gitee 与双引擎的自动化发布流水线

技术博客一键多发实战:基于 Gitee 与双引擎的自动化发布流水线

cross-write

文章目录

写一篇技术博客,然后手动复制到掘金、CSDN、知乎、微信公众号……这种重复劳动正在吞噬你的创作时间。本文将手把手带你搭建一套基于 Gitee + 文颜 + multi-publisher 的双引擎自动化发布系统,实现 git push 即全平台上线。本文所有操作均基于新版 Gitee Go(在线编排编辑器),无需在仓库中放置 .gitee-ci.yml 文件,所有流水线配置都在 Gitee 网页端完成。


一、为什么需要双引擎?

技术作者的发布矩阵通常包含两类平台:

  • 微信公众号:受众精准,但格式封闭,图片必须上传到自身素材库。
  • 技术社区:掘金、CSDN、知乎 等,开放性较好,但 Cookie 维护和反爬策略各不相同。

单靠一个工具很难同时兼顾这两类平台的稳定性与覆盖面。文颜在微信公众号的 API 集成上做得非常深入,合规且稳定;而 multi-publisher 则能覆盖 20+ 技术社区,实现广度分发。将二者组合为“双引擎”,就能在一个 Git 仓库内,让每篇文章自动适配不同平台的格式、图片和发布方式,真正实现 Write once, publish everywhere


二、项目结构与文章规范

在动手之前,先约定好 Git 仓库的目录结构。清晰的模块划分是自动化流程能被准确触发和追溯的基础。

my-blog-workspace/
├── posts/                   # [核心] 存放所有待发布的 Markdown 文件
│   ├── my-new-article.md
│   └── another-article.md
├── package.json             # [重要] 声明 wenyan 和 multi-publisher 依赖,供 CI 安装
├── .gitignore
└── README.md

关于 Gitee Go 配置文件:新版 Gitee Go 使用在线编排编辑器,无需在仓库根目录放置 .gitee-ci.yml。你只需在 Gitee 流水线页面中直接编辑 YAML 内容即可。若你仍希望保留本地备份,可命名为 .gitee-ci.yml.bak 或类似名称,但不会被自动读取。

为了让工具自动识别标题、标签、封面以及发布状态,每篇 Markdown 文章都必须以 YAML Front Matter 开头。它是整条流水线的“身份证”。

---
title: "构建个人博客的 CI/CD 自动发布流水线"
date: 2026-05-24
tags: [DevOps, CI/CD, 自动化]
cover: "https://your-image-bed.com/cover-image.jpg"
status: # [可选] 记录各平台发布状态
  juejin: ""
  zhihu: ""
  csdn: ""
  weixin: ""
---
  • title / date / tags:会被自动提取并填写到对应平台的编辑框。
  • cover:统一使用外链图片,建议搭建图床(如阿里云 OSS、腾讯云 COS)。文章中的图片同样使用外链,这样在支持外链的平台可以直接显示,公众号等封闭平台则由工具自动下载并转存。
  • status:每次发布成功后,将平台返回的文章 ID 或链接写回此处,避免重复发布。
  • source_url(可选):如果你已有独立博客,且文章首发在博客上,可在此填入博客原文链接,便于技术社区识别原创来源。如果是首次发布、尚无独立博客,建议直接删除这一行,不必填写占位链接,以免平台抓取无效地址或影响原创判定。

三、本地环境与核心工具安装

3.1 准备 Node.js 环境

本地计算机需安装 Node.js v18.20+。在终端中验证:

node --version
npm --version

3.2 全局安装双引擎

# 负责微信公众号稳定发布
npm install -g @wenyan-md/cli
wenyan --version

# 负责其他技术社区广度分发
npm install -g multi-publisher
mpub --version

⚠️ 关于 package.json 的重要说明

本地测试时,我们使用 npm install -g 全局安装两个工具,所以可以直接在终端运行命令。

但在 CI/CD 流水线中,脚本里执行的是 npm install(不带 -g),这是本地安装。如果不把依赖声明在 package.json 中,流水线执行 npm install 将不会安装任何工具,后续的 wenyanmpub 命令会报错 command not found

因此,请务必在项目根目录创建 package.json,并将两个工具写入 devDependencies

{
"name": "my-blog-workspace",
"version": "1.0.0",
"description": "自动化发布博客流水线",
"devDependencies": {
 "@wenyan-md/cli": "latest",
 "multi-publisher": "latest"
},
"scripts": {
 "publish": "wenyan publish && mpub publish"
}
}

这样,流水线中的 npm install 就会正确安装依赖,且 node_modules/.bin 会自动加入 PATH,CI 环境可以直接识别 wenyanmpub 命令,无需添加 npx 前缀。

说明scripts.publish 是本地调试用的快捷命令。当你在本地写完几篇文章,想测试一下工具能不能正常工作,但不想走 git push 触发 CI 时,可以直接在项目根目录敲 npm run publish,它会把你 posts/ 目录下所有文章都发一遍(前提是你的工具支持默认路径)。Gitee Go 流水线中直接调用带参数的原始命令(-f 指定具体文件),不依赖此脚本。


四、获取并配置各平台凭证

这是整条流水线最关键的一步。不同平台使用不同的认证方式,需要逐个配置。

4.1 配置微信公众号身份凭据(文颜负责)

文颜使用官方 API 发布到草稿箱,因此需要 AppIDAppSecret

  1. 登录微信公众号后台,进入「设置与开发」→「基本配置」
  2. 复制 AppID
  3. 前往 微信开发者平台,进入「首页」→「公众号」→「开发密钥」→「AppSecret」→「开启」→ 「管理员扫码重置」
  4. 复制 AppSecret (建议永久保存,否则下一次仍需重置)
  5. 在「基本配置」页面下方,找到 IP 白名单,将后续 CI/CD 运行环境的公网出口 IP 加入白名单,否则 API 调用会被拒绝

如何获取 CI 运行环境的 IP?
可以先不加白名单,运行一次流水线,在微信后台的调用日志中会看到被拒绝的 IP,再将其添加即可。

4.2 配置其他技术社区身份凭据(multi-publisher 负责)

multi-publisher 通过保存登录后的 Cookie 来模拟用户操作。在本地终端(cmd/PowerShell)中依次执行:

mpub login -p zhihu
mpub login -p juejin
mpub login -p csdn
# ... 按需登录其他平台

每执行一条命令,工具会打开浏览器窗口让你完成登录授权,如下图,登录成功后,Cookie 会被安全地加密保存在本地 ~/.mpub/ 目录下。

img

4.3 本地凭证存储位置

multi-publisher 登录成功后,会将 Cookie 加密保存在配置文件中(实际为明文 JSON,但建议不要手动编辑):

  • Linux/macOS~/.config/multi-publisher/config.json
  • WindowsC:\Users\[你的用户名]\.config\multi-publisher\config.json

你可以通过以下命令查看确切的配置文件路径:

mpub credential --location

img

打开配置文件你会发现,cookies 字段是一个 JSON 对象(例如 {"csrf_session_id":"abc", "s_v_web_id":"xyz"})。然而,在 CI/CD 环境变量中,multi-publisher 需要接收的是 标准的 HTTP Cookie 字符串,即 key1=value1; key2=value2 的形式。直接将整个 JSON 对象粘贴到 Gitee 变量中是无效的,因此我们必须先将本地的 Cookie 转换成这种字符串格式。下面提供两种简单的方法来完成这一转换。

4.4 如何将 Cookie 的 JSON 对象格式化成标准的 HTTP Cookie 字符串?

multi-publisher 没有内置的导出命令mpub credential export 并不支持 -p 参数),但你可以通过读取配置文件来生成符合 HTTP Cookie 规范的字符串。

方法1:使用 jq 命令格式化 Cookie JSON 对象

jq 是一款轻量级的命令行 JSON 处理器,安装后可以用一条命令直接提取并拼接 Cookie。
如果你不想安装 jq,也可以使用文末的 Node.js 脚本(跨平台通用,无需额外安装)。

安装 jq 提示

  • macOSbrew install jq
  • Linux (Debian/Ubuntu)sudo apt install jq
  • Windows:可用 winget install jqlang.jq 或下载可执行文件加入 PATH(具体可自行搜索)。

🐧 macOS / Linux(bash/zsh)

# 掘金
jq -r '.juejin.cookies | to_entries | map(.key + "=" + .value) | join("; ")' ~/.config/multi-publisher/config.json

# CSDN
jq -r '.csdn.cookies | to_entries | map(.key + "=" + .value) | join("; ")' ~/.config/multi-publisher/config.json

# 知乎
jq -r '.zhihu.cookies | to_entries | map(.key + "=" + .value) | join("; ")' ~/.config/multi-publisher/config.json

🪟 Windows CMD推荐 Windows 用户使用此方式

REM 掘金
jq -r ".juejin.cookies | to_entries | map(.key + \"=\" + .value) | join(\"; \")" %USERPROFILE%\.config\multi-publisher\config.json

REM CSDN
jq -r ".csdn.cookies | to_entries | map(.key + \"=\" + .value) | join(\"; \")" %USERPROFILE%\.config\multi-publisher\config.json

REM 知乎
jq -r ".zhihu.cookies | to_entries | map(.key + \"=\" + .value) | join(\"; \")" %USERPROFILE%\.config\multi-publisher\config.json

⚠️ Windows PowerShell 用户请注意
PowerShell 环境下执行 jq 命令存在引号解析和转义问题,强烈建议使用上述 CMD 命令,或直接使用下面的 Node.js 脚本(跨平台,无需关心引号)。
如果你在 PowerShell 中执行 jq 遇到 syntax error 等报错,属于正常现象,请切换到 CMD 或使用 Node.js 方案。

执行后,终端会输出一行完整的 Cookie 字符串(例如:csrf_session_id=xxx; s_v_web_id=yyy; ...),直接复制即可。

img


方法2:使用 Node.js 脚本格式化 Cookie JSON 对象

如果 jq 命令执行报错或你不想额外安装工具,可以用 Node.js 脚本(因为 multi-publisher 本身依赖 Node,你的电脑一定有 Node 环境)。
创建一个文件 export-cookie.js,内容如下:

const fs = require('fs');
const path = require('path');
const os = require('os');
const configPath = path.join(os.homedir(), '.config', 'multi-publisher', 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const platform = process.argv[2] || 'juejin'; // 从命令行参数获取平台名
const cookies = config[platform]?.cookies;
if (!cookies) {
  console.error(`平台 ${platform} 未找到,请检查配置文件`);
  process.exit(1);
}
const cookieString = Object.entries(cookies).map(([k, v]) => k + '=' + v).join('; ');
console.log(cookieString);

然后执行(任何系统终端均可):

node export-cookie.js juejin

输出的 Cookie 字符串同样可直接复制使用,无需关心操作系统差异

无论使用哪种方式,将得到的字符串填入 Gitee 环境变量时,类型务必选择 “密文”


五、开启 Gitee Go 流水线(新版操作流程)

在开始编写流水线之前,需要先在 Gitee 仓库中开通并创建流水线。Gitee Go 是 Gitee 官方推出的 CI/CD 工具,提供持续集成与持续交付能力。本节所有操作基于新版 Gitee Go 的可视化编排界面

5.1 前置条件

  • 你的 Gitee 账号必须绑定手机号(否则无法开通 Gitee Go)。
  • 确保仓库中存在 posts/ 目录和至少一篇 Markdown 文章(用于测试)。

5.2 开通 Gitee Go 服务

  1. 打开你的 Gitee 代码仓库,在顶部导航栏中找到 “流水线” 标签页并点击。
  2. 若你尚未开通 Gitee Go,系统会引导你点击“开通”按钮。开通后,单个仓库可获得 200 分钟 免费构建时长(永久有效),企业/组织/个人每月还有 500 分钟 免费额度自动到账。

5.3 创建第一条流水线

开通成功后,你会进入流水线列表页面(可能显示为“空白流水线”)。此时请按以下步骤操作:

  1. 点击 “新建流水线” 按钮。
  2. 在模板选择界面,选择 “空模板”(不要选择任何预置模板)。
  3. 接着会进入 “新建流水线” 的在线编辑页面,你会看到一个默认的 YAML 模板(内容类似 version: '1.0' 开头)。
  4. 此时无需任何额外配置,直接进入下一步——我们将把完整的 YAML 内容粘贴到编辑器中

注意:新版 Gitee Go 的流水线配置完全由在线编辑器中的 YAML 内容决定,不再读取仓库根目录下的 .gitee-ci.yml 文件。因此,你只需在该编辑器中编写或粘贴配置即可。


六、在 Gitee 仓库中注册 CI/CD 变量

流水线中需要使用的敏感信息(如微信 AppID、各平台 Cookie 等)不应硬编码在配置文件中,而应通过 Gitee 的环境变量功能来管理。环境变量是 Gitee 仓库提供的通用环境变量管理功能,可用于管理流水线中的口令和密钥信息。

6.1 进入变量设置页面

打开你的 Gitee 代码仓库,进入 “流水线”“通用变量”

6.2 添加变量

点击 “添加变量” 按钮,按照提示填写变量信息:

  • 变量名:输入纯英文标识符,例如 WECHAT_APP_ID。注意,变量名不能以 GITEE_GO_ 开头,这些是系统保留字。
  • :粘贴对应的实际凭证内容。
  • 类型:对于密码、Token、Cookie 等敏感信息,请务必选择 “密文” 类型,这样值在页面上会被隐藏,避免泄露。

6.3 保存并重复

点击确认创建。重复以上步骤,依次添加你在第四节中获取到的所有凭证:

变量名说明
WECHAT_APP_ID微信公众号 AppID
WECHAT_APP_SECRET微信公众号 AppSecret
JUEJIN_COOKIE掘金平台 Cookie
CSDN_COOKIECSDN 平台 Cookie
ZHIHU_COOKIE知乎平台 Cookie

img

💡 如何为 multi-publisher 平台准备正确的 Cookie 值?
在 4.2 节中我们已经介绍了从本地配置文件导出标准 Cookie 字符串的方法。请使用 jq 或 Node.js 脚本 生成完整的 key=value; ... 字符串,然后将其粘贴到对应变量的 “值” 框中。切勿直接粘贴 JSON 对象或仅粘贴部分键值对,否则 multi-publisher 将无法正常登录。

各平台对应的变量名如下:

  • 掘金 → JUEJIN_COOKIE
  • CSDN → CSDN_COOKIE
  • 知乎 → ZHIHU_COOKIE

如果后期 Cookie 过期,只需在本地重新登录(mpub login -p <平台>),再次运行导出脚本,更新 Gitee 变量即可,流水线配置无需改动。

6.4 在流水线中引用变量

变量添加完成后,在流水线 YAML 中通过 $变量名 的形式直接引用即可。例如在后续的 command 脚本中使用 export WECHAT_APP_ID="$WECHAT_APP_ID"。Gitee Go 在执行流水线时会自动将这些变量注入到运行环境中。


七、搭建 CI/CD 流水线:双引擎实战(新版 YAML)

现在我们来编写核心的自动化脚本。新版 Gitee Go 使用在线 YAML 编辑,无需在仓库中创建 .gitee-ci.yml。你只需将以下完整配置复制,并粘贴到第 5.3 步中打开的在线编辑器中,然后保存即可。

7.1 在线流水线配置(适配新版 Gitee Go)

在流水线编辑器中,完全删除默认生成的示例代码,然后粘贴以下 YAML:

version: '1.0'
name: auto-publish-blog
displayName: 技术博客一键发布流水线
triggers:
  push:
    branches:
      prefix:
        - master
stages:
  - stage: publish
    steps:
      - step: build@nodejs
        name: 发布文章到多平台
        nodeVersion: '20'
        commands:
          - |
            # 1. 确保完整 Git 历史 (安全)
            git fetch --unshallow || true

            # 2. 安装依赖 (建议在 package.json 中声明)
            npm install

            # 3. 获取变更的 Markdown 文件
            if [ "$GITEE_EVENT" = "workflow_dispatch" ]; then
              if [ -n "$INPUT_FILES" ]; then
                FILES="$INPUT_FILES"
              else
                FILES=$(find posts -name "*.md" | tr '\n' ' ')
              fi
            else
              CHANGED_FILES=$(git diff --name-only HEAD^ HEAD | grep 'posts/.*\.md$' | tr '\n' ' ')
              FILES="$CHANGED_FILES"
            fi

            if [ -z "$FILES" ]; then
              echo "没有需要发布的文件,退出"
              exit 0
            fi

            # 4. 设置认证信息 (使用你在通用变量中配置的密文)
            export WECHAT_APP_ID="$WECHAT_APP_ID"
            export WECHAT_APP_SECRET="$WECHAT_APP_SECRET"
            export JUEJIN_COOKIE="$JUEJIN_COOKIE"
            export CSDN_COOKIE="$CSDN_COOKIE"
            export ZHIHU_COOKIE="$ZHIHU_COOKIE"

            # 5. 循环发布
            FAILED_PLATFORMS=""
            for file in $FILES; do
              echo "📝 开始发布:$file"

              echo "🚀 文颜引擎 → 微信公众号"
              if ! wenyan publish -f "$file"; then
                FAILED_PLATFORMS="$FAILED_PLATFORMS WeChat"
                echo "❌ 文颜引擎发布失败" >&2
              fi

              echo "🚀 multi-publisher → 掘金、CSDN、知乎"
              if ! mpub publish -f "$file" -p juejin,zhihu,csdn; then
                FAILED_PLATFORMS="$FAILED_PLATFORMS Multi"
                echo "❌ multi-publisher 发布失败" >&2
              fi

              echo "✅ 完成发布:$file"
            done

            if [ -n "$FAILED_PLATFORMS" ]; then
              echo "⚠️ 警告:以下平台发布可能失败:$FAILED_PLATFORMS"
            fi

            # 6. 提交状态更新 (需拥有推送权限)
            git config --global user.name "CI Bot"
            git config --global user.email "ci-bot@example.com"
            git add posts/
            git commit -m "ci: 更新文章发布状态 [skip ci]" || echo "没有状态变更"
            if ! git push origin master; then
              echo "⚠️ 警告:推送状态更新失败,请检查 CI 推送权限" >&2
            fi

配置解读

  • version: '1.0':固定格式,表示使用新版流水线语法。
  • triggers.push.branches.prefix: - main:当 main 分支有推送时触发流水线。
  • stages:定义一个发布阶段,内部使用 nodejs-build 任务,指定 Node.js 20 版本。
  • command 中的脚本内容与旧版基本相同,但注意环境变量引用方式不变(直接使用 $变量名),这些变量来自你在第 6 章配置的“通用变量”。
  • 支持手动触发(workflow_dispatch)的逻辑依然保留,但新版 Gitee Go 中手动触发需要通过界面上的“运行”按钮实现,并可以填写 INPUT_FILES 参数(具体用法见下文)。

7.2 保存并验证流水线

  1. 粘贴完成后,点击页面底部的 “保存” 按钮(可能会显示“保存并运行”)。
  2. 保存成功后,你可以点击 “运行” 按钮手动触发一次构建,以验证配置是否正确。
  3. 检查构建日志,确保没有语法错误或依赖安装失败。

7.3 推送触发与日常使用

现在,你的日常创作流程就简化为了:

  1. 在本地用 Typora、VS Code 或 Obsidian 写文章,图片自动上传至图床。

  2. 填写 Front Matter,保存到 posts/ 目录。

  3. 执行:

    git add posts/新文章.md
    git commit -m "新文章: xxx"
    git push
    
  4. 几秒钟后,Gitee Go 会自动检测到 main 分支的推送,并启动流水线。你可以在“流水线”页面查看实时构建日志。

  5. 构建成功后,文章就会自动出现在微信公众号草稿箱,以及你配置好的掘金、CSDN、知乎等社区,封面、标签一应俱全。

注意:由于新版 Gitee Go 不再读取仓库中的 .gitee-ci.yml,你的所有流水线配置都在 Gitee 网页端维护。如需修改触发分支或发布脚本,直接编辑在线 YAML 并保存即可,无需再次提交代码。

手动触发指定文件:若你只想发布某几篇文章,可以在流水线页面点击“运行”,在弹出的对话框中填写 INPUT_FILES 参数,例如 posts/my-new-article.md posts/another.md,流水线将只发布这些文件(留空则发布全部)。


八、进阶功能与长期维护

8.1 分平台管理文章状态,防止重复发布

发布成功后,建议将返回的文章 ID 写回 Front Matter 的 status 字段,并提交回仓库。例如:

status:
  juejin: "https://juejin.cn/post/1234567890"
  zhihu: "https://zhuanlan.zhihu.com/p/1234567890"
  weixin: "https://mp.weixin.qq.com/s/xxxxxx"

在下次流水线触发时,脚本可以增加判断逻辑:若对应平台已有 ID,则跳过或执行“更新”而非“重新发布”,避免产生重复文章。

8.2 定时巡检与 Token 有效期维护

Cookie 过期是多平台分发的最大敌人。可以通过创建一个定时触发的流水线,配合 multi-publisher 的内置命令来监控状态:

mpub cookie --platform juejin --check

如果检测到凭证失效,流水线通过企业微信、钉钉或飞书机器人发送通知,提醒你重新登录并更新 Secrets。

8.3 发布结果通知

在流水线 YAML 的 command 脚本最后,添加一个通知脚本,调用你习惯的 IM 工具的 Webhook 接口,将发布结果(成功或失败)及时推送给自己,让你能第一时间处理异常。


九、常见问题 Q&A

Q1:我没有 Gitee,可以用 GitHub Actions 吗?

完全可以。两者的 CI/CD 语法高度相似,你只需将配置文件改为 .github/workflows/publish.yml,并调整少数关键字(如 runs-on、Secrets 引用方式),核心脚本几乎不用改动。GitHub Actions 在访问 Medium、Dev.to 等海外平台时网络条件更优。

Q2:multi-publisher 支持 20+ 平台,如何只发布到指定的几个?

使用 -p 参数即可精确指定目标平台:

mpub publish -f my-article.md -p juejin,csdn

Q3:如何只发布新增或修改的文章,而不是全量发布?

流水线配置中的「获取变更的 Markdown 文件」步骤就是为此而设计。它通过 git diff 只识别出本次推送涉及到的 md 文件,实现 增量发布

Q4:文章中使用了图床外链,微信公众号无法显示怎么办?

这正是引入文颜的意义之一。文颜在执行发布时,会自动将 Markdown 中的外部图片链接下载并上传到微信公众号素材库,然后用返回的 mmbiz.qpic.cn 链接替换原文,全程无需手动操作。

Q5:遇到发布失败如何调试?

  1. 优先查看 Gitee Go 的详细日志输出。
  2. 在本地重现命令,例如 wenyan publish -f your-article.md,通过错误信息定位问题。
  3. 访问对应工具的 GitHub 仓库,查看已有 Issues 或提交新问题。

Q6:我按照旧版教程放了 .gitee-ci.yml,但新版 Gitee Go 不触发怎么办?

新版 Gitee Go 采用在线编排编辑器,不再读取仓库中的 .gitee-ci.yml。请按照第 5 章和第 7 章的方法,在流水线页面中新建空模板,粘贴本文提供的 YAML 配置并保存。之后所有推送都会触发该流水线。原有的 .gitee-ci.yml 文件可删除或作为备份保留,不影响流水线运行。


十、结语

从“写完一篇技术博客,花半小时搬运到各个平台”,到“git push 之后静待花开”,这条自动化之路在今天已经非常成熟。双引擎(文颜 + multi-publisher)的组合,既保证了对微信公众号这一封闭生态的稳定输出,又兼顾了众多技术社区的广泛触达。

一旦搭建完成,你就能把时间真正还给思考和创作,而不再被格式、图片和登录这些琐事打断。现在就动手,享受一次 git push 然后全平台上线带来的畅快体验吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YahirQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值