Joplin 发布部署全流程:从 setupNewRelease 到各应用发布脚本的源码级解析
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
Joplin 仓库同时维护桌面端、Android、iOS、CLI、Server、Web 剪藏器、插件生成器等多个可发布产物,它们的版本对齐与发布流程被集中封装在packages/tools目录下的一组脚本中。本文基于仓库文档 DEPLOY.md 与对应发布脚本源码展开,覆盖版本号统一设置、各应用发布命令的实际执行链路、changelog 自动写入机制等细节,读完即可完整理解 Joplin 从“准备一个 minor 版本”到“各产物逐一发布”的全过程。
一、发布脚本总览
所有发布入口都定义在仓库根目录 package.json 的scripts字段中,每个release*脚本最终都映射到packages/tools下的一个 Node 脚本:
| yarn 脚本 | 底层实现文件 | 发布对象 |
|---|---|---|
yarn setupNewRelease | setupNewRelease.ts | 全部包/配置版本号 |
yarn releaseDesktop | release-electron.ts | 桌面端(CI 构建) |
yarn releaseAndroid | release-android.ts | Android APK |
yarn releaseCli | release-cli.ts | CLI(npm) |
yarn publishAll | gulpfile.js 中completePublishAll | 全部@joplin/*库(npm) |
yarn releaseServer | release-server.ts | Joplin Server |
yarn releaseClipper | release-clipper.ts | Web 剪藏器扩展 |
yarn releasePluginGenerator | release-plugin-generator.js | 插件生成器(npm) |
yarn releasePluginRepoCli | release-plugin-repo-cli.ts | 插件仓库 CLI(npm) |
根目录 package.json 的engines字段声明了发布环境的最低要求:Node>=22.12、Yarn4.14.1(packageManager为yarn@4.16.0)。仓库通过 Yarn workspaces(packages/*)组织所有子包,这是理解发布顺序依赖关系的前提。
二、发布前置:用 setupNewRelease 统一版本号
按照 DEPLOY.md 的说明,创建任何新发布之前,都必须先更新全部版本号,命令为:
yarn setupNewRelease 1.8该命令传入的是新的major.minor版本号,patch 号在后续各包单独发布时自动递增。
从源码 setupNewRelease.ts 可以看到它具体做了哪些事:
- 批量更新 19 个包的
package.json:脚本依次处理app-cli、app-desktop、app-mobile、generator-joplin、htmlpack、lib、pdf-viewer、plugin-repo-cli、react-native-alarm-notification、react-native-saf-x、renderer、server、tools、utils、onenote-converter、default-plugins、editor、transcribe、whisper-voice-typing,将version字段设置为majorMinor.0; - 同步内部依赖版本:对每个包的
dependencies和devDependencies中所有@joplin/*前缀的依赖(排除@joplin/turndown、@joplin/turndown-plugin-gfm和@joplin/fork-*),统一改写为~majorMinor形式; - 更新原生工程配置:
- Android:改写 build.gradle 中的
versionName为majorMinor.0; - iOS:改写 project.pbxproj 中的
MARKETING_VERSION; - Web 剪藏器:更新 manifest.json 的
version; - 插件模板:更新 generator-joplin 模板 manifest 的
app_min_version,保证新生成的插件以新大版本为最低兼容版本。
- Android:改写 build.gradle 中的
几个值得注意的实现细节:
- iOS 版本前缀 hack:源码中的
iosVersionHack函数会把x.y.z转成1x.y.z,注释说明这是因为历史某次发布失误后,App Store 不允许降低大版本号,只能人为抬高; - 可选参数:通过
--updateVersion=0/--updateDependenciesVersion=0可以只执行其中一类更新;若两者都为 0 脚本会直接报 “Nothing to do!”; - 脚本执行完毕会提示运行
yarn install以刷新 lock 文件。
以当前仓库状态为例,app-cli/package.json 中@joplin/lib、@joplin/renderer、@joplin/utils的依赖均写作~3.7,与上述“依赖统一为 major.minor”的规则一致。
三、桌面端:releaseDesktop 依赖 CI,本地只负责打 tag
DEPLOY.md 描述:桌面应用通过持续集成构建 Windows、macOS、Linux 三个平台的安装包,触发方式是向 GitHub 推送版本 tag,本地命令为:
yarn releaseDesktop对照 release-electron.ts 的源码,这条命令的完整链路是:
gitPullTry拉取最新代码,进入packages/app-desktop;- 通过
versionPatch()(来自@joplin/utils/version)把当前包版本号的 patch 位加一,作为本次发布的版本号; git add -A、提交 “Desktop release <version>”、打同名的版本 tag 并git push --tags;- 调用
githubRelease('joplin', tagName, { isDraft: true, isPreRelease: true })在 GitHub 上创建一个草稿预发布,真正的多平台构建与附件上传由 CI 在 tag 推送后完成; - 打印后续操作提示:用
node packages/tools/git-changelog.js <version>生成 changelog,并把版本更新合并回dev分支。
也就是说,本地命令本身不编译 Electron 应用,它只负责“版本号 + tag + 草稿 release”,这是理解 Joplin 桌面端发布模型的关键。
四、Android:releaseAndroid 一条命令完成构建与上传
yarn releaseAndroid --type=prerelease--type参数取值为release或prerelease。从 release-android.ts 看,脚本的默认行为正是 prerelease(不传--type时isPreRelease为true),这与 DEPLOY.md 中“Android 只发布预发布版、后续再人工提升为稳定版”的注释一致。
源码中这条命令的执行链路:
- 版本号自增:直接改写 build.gradle——
versionCode数字 +1,versionName的第三位(patch)+1,并以versionName生成 tagandroid-v<version>; - 构建 APK:在仓库根目录依次执行
yarn install、yarn tsc、yarn buildParallel,然后在packages/app-mobile/android下运行./gradlew assembleRelease。源码注释特别说明不运行./gradlew clean,因为在 React Native 0.8x 上clean会触发 CMake 重新生成并因 autolinking/codegen 失败,改用yarn clean手动清理构建产物; - 产物整理:APK 从
android/app/build/outputs/apk/release/app-release.apk复制到packages/app-mobile/dist/joplin-v<version>.apk,主渠道(main)还会额外复制一份joplin-latest.apk; - 上传 GitHub Release:在
joplin-android项目下创建对应 tag 的 release,并用 OAuth token 上传 APK; - 写 changelog:调用
completeReleaseWithChangelog把本次改动写入 android.md。
脚本还支持--release-name参数只构建指定渠道;源码中定义了custom、armeabi-v7a、x86、arm64-v8a、x86_64等多个ReleaseConfig,其中分架构构建目前处于disabled: true状态,当前实际启用的是main与custom两个渠道。
五、iOS:手动使用 Xcode 发布
DEPLOY.md 明确说明 iOS 应用必须使用 Xcode 手动构建并发布。仓库中版本对齐工作已由setupNewRelease完成(更新MARKETING_VERSION),发布动作本身则不在脚本自动化范围内。
值得注意的是,仓库里其实存在 release-ios.ts 脚本,且根目录package.json中注册了releaseIOS;从源码结构看,可以推断该脚本承担的是与手动发布配套的部分流程(如版本号或 changelog 处理),但 DEPLOY.md 的口径仍以 Xcode 手动发布为准。
六、CLI:先 publishAll 发布全部内部库,再发 CLI 本体
DEPLOY.md 指出 CLI 与移动端/桌面端不同:它不打包依赖,始终从 npm 源码安装,因此所有@joplin/*依赖必须先公开发布。这一步由publishAll完成:
yarn publishAll对照根目录 package.json,publishAll的完整定义为:
git pull && yarn buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll拆解为四步:
git pull拉取最新代码;yarn buildParallel并行构建全部 workspace 包(底层是yarn workspaces foreach ... run build && yarn tsc);lerna version --yes --no-private --no-git-tag-version为所有非 private 的@joplin/*包提升 patch 版本号(不自动打 tag);gulp completePublishAll执行 gulpfile.js 中定义的任务:git commit版本变更 →lerna publish from-package -y --no-verify-access发布所有包(注释解释了--no-verify-access是为绕过自动化 token 下 Lerna 的多余鉴权检查,参见 lerna issue #2788)→yarn install并再次提交 lock 文件 →git push。
这一步会发布@joplin/lib、@joplin/renderer、@joplin/utils、@joplin/tools等全部 Joplin 内部库。
接下来按 DEPLOY.md 的要求,在 app-cli/package.json 中把所有@joplin的dependencies和devDependencies设置为新的 major/minor 版本(例如1.8,当前仓库实际值为~3.7):
"dependencies": { "@joplin/lib": "1.8", "@joplin/renderer": "1.8", "...": "..." }, "devDependencies": { "@joplin/tools": "1.8", "...": "..." }最后发布 CLI 本体:
yarn releaseClirelease-cli.ts 的执行流程:git pull→versionPatch()自增版本并生成 tagcli-<version>→ 进入packages/app-cli执行yarn build(即gulp build)并复制 README 到build/→ 在build/目录中npm publish→ 调用completeReleaseWithChangelog把 changelog 写入 cli.md。脚本头注释还提到可用node packages/tools/release-cli.js --changelog-from cli-vX.Y.Z手动指定 changelog 的起始 tag。
七、Joplin Server:releaseServer
yarn releaseServerrelease-server.ts 的链路相对简洁:gitPullTry→ 进入packages/server→versionPatch()自增版本 → 生成 tagserver-<version>→completeReleaseWithChangelog把 changelog 写入 server.md。tag 推送后由 CI 完成 Server 的实际构建(镜像构建相关脚本见 buildServerDocker.ts),本地命令同样只负责版本与 tag。
八、Web 剪藏器:releaseClipper
yarn releaseClipperrelease-clipper.ts 的完整流程:
- 自增版本号:读取 manifest.json,把
version的最后一位(build number)加一并写回; - 构建扩展:切换
util/joplinEnv.mjs为prod模式(该文件被标记为// AUTOGENERATED by release-clipper),在packages/app-clipper/popup下npm run build; - 生成双平台发行包:分别组装
dist/chrome与dist/firefox,两者的差异在manifest.json上——Chrome 包删除browser_specific_settings并去掉background.scripts/persistent(使用 MV3 service worker),Firefox 包则删除background.service_worker(使用 scripts 形式);随后各打成一个 zip; - 源码包校验:额外打一个
joplin-webclipper-source.zip(排除node_modules/、build/、dist/),用于浏览器商店的代码审查,并且脚本会在临时目录里重新npm install && npm run build,逐文件比对重新编译结果与已发布的 Firefox 包(MD5 对比),确保“发布的二进制与开源源码可复现一致”; - 打 tag:若无
--no-publish参数,则git commit+git tag clipper-<version>+ 推送;最后把joplinEnv.mjs切回dev模式。
九、插件生成器与插件仓库 CLI
插件生成器(generator-joplin)
按 DEPLOY.md,发布前一般先更新类型定义:
./updateTypes.sh然后:
yarn releasePluginGeneratorrelease-plugin-generator.js 的自动化程度较高:进入packages/generator-joplin后,它会自己执行一次bash updateTypes.sh,versionPatch()自增版本,把package.json的private字段临时去掉(setPackagePrivateField)以允许npm publish,finally中保证恢复为true,最后提交、打 tagplugin-generator-<version>并推送。仓库根目录package.json中还注册了updatePluginTypes脚本指向 updateTypes.sh。
插件仓库 CLI(plugin-repo-cli)
DEPLOY.md 说明该工具用 Webpack 打包,因此单条命令即可发布:
yarn releasePluginRepoCli对照 release-plugin-repo-cli.ts:根目录yarn tsc编译类型 → 进入packages/plugin-repo-cli执行yarn dist(即 package.json 中定义的webpack --config webpack.config.js)→versionPatch()自增版本 →npm publish→ 打印plugin-repo-cli-<version>tag 对应的收尾 git 命令。
十、通用收尾机制:changelog 自动写入与收尾命令
多个发布脚本(CLI、Android、Server 等)结尾都会调用 tool-utils.ts 中的completeReleaseWithChangelog,它的工作方式是:
- 执行
node packages/tools/git-changelog <tag> --publish-format full从 git 提交记录生成该版本的变更清单(实现见 git-changelog.ts); - 将结果以
## <tag>加 UTC 时间戳的格式插入到对应 changelog 文件顶部(各产物对应 changelog 目录 下的cli.md、android.md、server.md等文件); - 打印提示:先用
$EDITOR人工核对 changelog,再执行releaseFinalGitCommands生成的收尾命令链——
git add -A && git commit -m "<AppName> <version>" && git tag "<tag>" && git push && git push origin refs/tags/<tag>这解释了 Joplin 发布的最后一个共性环节:脚本负责构建、发布、写 changelog,而最终的版本 tag 提交普遍需要人工核对后执行(桌面端例外,它在脚本内直接完成了 tag 与 push)。
小结
Joplin 的发布体系可以概括为三层:
- 版本层:
setupNewRelease一次性对齐 19 个 npm 包、Gradle、Xcode、剪藏器 manifest 与插件模板的版本号,patch 号留待各包发布时自动递增; - 发布层:每个产物对应
packages/tools/release-*.ts脚本,桌面端与 Server 走“本地打 tag + CI 构建”,Android/CLI/插件工具走“本地构建 + 直接上传/npm publish”;CLI 发布前必须通过publishAll(Lerna 驱动)先把全部@joplin/*库发布到 npm; - 收尾层:
completeReleaseWithChangelog统一处理 changelog 写入与收尾 git 命令,人负责最终核对。
所有命令的适用前提与 DEPLOY.md 一致:需在完整的 Joplin monorepo 根目录下执行,且环境满足根 package.json 声明的 Node/Yarn 版本要求。
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考