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

文章目录
写一篇技术博客,然后手动复制到掘金、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将不会安装任何工具,后续的wenyan和mpub命令会报错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 环境可以直接识别wenyan和mpub命令,无需添加npx前缀。
说明:
scripts.publish是本地调试用的快捷命令。当你在本地写完几篇文章,想测试一下工具能不能正常工作,但不想走git push触发 CI 时,可以直接在项目根目录敲npm run publish,它会把你posts/目录下所有文章都发一遍(前提是你的工具支持默认路径)。Gitee Go 流水线中直接调用带参数的原始命令(-f指定具体文件),不依赖此脚本。
四、获取并配置各平台凭证
这是整条流水线最关键的一步。不同平台使用不同的认证方式,需要逐个配置。
4.1 配置微信公众号身份凭据(文颜负责)
文颜使用官方 API 发布到草稿箱,因此需要 AppID 和 AppSecret。
- 登录微信公众号后台,进入「设置与开发」→「基本配置」
- 复制 AppID
- 前往 微信开发者平台,进入「首页」→「公众号」→「开发密钥」→「AppSecret」→「开启」→ 「管理员扫码重置」
- 复制 AppSecret (建议永久保存,否则下一次仍需重置)
- 在「基本配置」页面下方,找到 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/ 目录下。

4.3 本地凭证存储位置
multi-publisher 登录成功后,会将 Cookie 加密保存在配置文件中(实际为明文 JSON,但建议不要手动编辑):
- Linux/macOS:
~/.config/multi-publisher/config.json - Windows:
C:\Users\[你的用户名]\.config\multi-publisher\config.json
你可以通过以下命令查看确切的配置文件路径:
mpub credential --location

打开配置文件你会发现,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 提示:
- macOS:
brew 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; ...),直接复制即可。

方法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 服务
- 打开你的 Gitee 代码仓库,在顶部导航栏中找到 “流水线” 标签页并点击。
- 若你尚未开通 Gitee Go,系统会引导你点击“开通”按钮。开通后,单个仓库可获得 200 分钟 免费构建时长(永久有效),企业/组织/个人每月还有 500 分钟 免费额度自动到账。
5.3 创建第一条流水线
开通成功后,你会进入流水线列表页面(可能显示为“空白流水线”)。此时请按以下步骤操作:
- 点击 “新建流水线” 按钮。
- 在模板选择界面,选择 “空模板”(不要选择任何预置模板)。
- 接着会进入 “新建流水线” 的在线编辑页面,你会看到一个默认的 YAML 模板(内容类似
version: '1.0'开头)。 - 此时无需任何额外配置,直接进入下一步——我们将把完整的 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_COOKIE | CSDN 平台 Cookie |
ZHIHU_COOKIE | 知乎平台 Cookie |

💡 如何为
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 保存并验证流水线
- 粘贴完成后,点击页面底部的 “保存” 按钮(可能会显示“保存并运行”)。
- 保存成功后,你可以点击 “运行” 按钮手动触发一次构建,以验证配置是否正确。
- 检查构建日志,确保没有语法错误或依赖安装失败。
7.3 推送触发与日常使用
现在,你的日常创作流程就简化为了:
-
在本地用 Typora、VS Code 或 Obsidian 写文章,图片自动上传至图床。
-
填写 Front Matter,保存到
posts/目录。 -
执行:
git add posts/新文章.md git commit -m "新文章: xxx" git push -
几秒钟后,Gitee Go 会自动检测到
main分支的推送,并启动流水线。你可以在“流水线”页面查看实时构建日志。 -
构建成功后,文章就会自动出现在微信公众号草稿箱,以及你配置好的掘金、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:遇到发布失败如何调试?
- 优先查看 Gitee Go 的详细日志输出。
- 在本地重现命令,例如
wenyan publish -f your-article.md,通过错误信息定位问题。 - 访问对应工具的 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 然后全平台上线带来的畅快体验吧。

310

被折叠的 条评论
为什么被折叠?



