Joplin 发布部署全流程:从 setupNewRelease 到各应用发布脚本的源码级解析
2026/9/14 12:13:53 网站建设 项目流程

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 setupNewReleasesetupNewRelease.ts全部包/配置版本号
yarn releaseDesktoprelease-electron.ts桌面端(CI 构建)
yarn releaseAndroidrelease-android.tsAndroid APK
yarn releaseClirelease-cli.tsCLI(npm)
yarn publishAllgulpfile.js 中completePublishAll全部@joplin/*库(npm)
yarn releaseServerrelease-server.tsJoplin Server
yarn releaseClipperrelease-clipper.tsWeb 剪藏器扩展
yarn releasePluginGeneratorrelease-plugin-generator.js插件生成器(npm)
yarn releasePluginRepoClirelease-plugin-repo-cli.ts插件仓库 CLI(npm)

根目录 package.json 的engines字段声明了发布环境的最低要求:Node>=22.12、Yarn4.14.1packageManageryarn@4.16.0)。仓库通过 Yarn workspaces(packages/*)组织所有子包,这是理解发布顺序依赖关系的前提。

二、发布前置:用 setupNewRelease 统一版本号

按照 DEPLOY.md 的说明,创建任何新发布之前,都必须先更新全部版本号,命令为:

yarn setupNewRelease 1.8

该命令传入的是新的major.minor版本号,patch 号在后续各包单独发布时自动递增。

从源码 setupNewRelease.ts 可以看到它具体做了哪些事:

  1. 批量更新 19 个包的package.json:脚本依次处理app-cliapp-desktopapp-mobilegenerator-joplinhtmlpacklibpdf-viewerplugin-repo-clireact-native-alarm-notificationreact-native-saf-xrendererservertoolsutilsonenote-converterdefault-pluginseditortranscribewhisper-voice-typing,将version字段设置为majorMinor.0
  2. 同步内部依赖版本:对每个包的dependenciesdevDependencies中所有@joplin/*前缀的依赖(排除@joplin/turndown@joplin/turndown-plugin-gfm@joplin/fork-*),统一改写为~majorMinor形式;
  3. 更新原生工程配置
    • Android:改写 build.gradle 中的versionNamemajorMinor.0
    • iOS:改写 project.pbxproj 中的MARKETING_VERSION
    • Web 剪藏器:更新 manifest.json 的version
    • 插件模板:更新 generator-joplin 模板 manifest 的app_min_version,保证新生成的插件以新大版本为最低兼容版本。

几个值得注意的实现细节:

  • 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 的源码,这条命令的完整链路是:

  1. gitPullTry拉取最新代码,进入packages/app-desktop
  2. 通过versionPatch()(来自@joplin/utils/version)把当前包版本号的 patch 位加一,作为本次发布的版本号;
  3. git add -A、提交 “Desktop release <version>”、打同名的版本 tag 并git push --tags
  4. 调用githubRelease('joplin', tagName, { isDraft: true, isPreRelease: true })在 GitHub 上创建一个草稿预发布,真正的多平台构建与附件上传由 CI 在 tag 推送后完成;
  5. 打印后续操作提示:用node packages/tools/git-changelog.js <version>生成 changelog,并把版本更新合并回dev分支。

也就是说,本地命令本身不编译 Electron 应用,它只负责“版本号 + tag + 草稿 release”,这是理解 Joplin 桌面端发布模型的关键。

四、Android:releaseAndroid 一条命令完成构建与上传

yarn releaseAndroid --type=prerelease

--type参数取值为releaseprerelease。从 release-android.ts 看,脚本的默认行为正是 prerelease(不传--typeisPreReleasetrue),这与 DEPLOY.md 中“Android 只发布预发布版、后续再人工提升为稳定版”的注释一致。

源码中这条命令的执行链路:

  1. 版本号自增:直接改写 build.gradle——versionCode数字 +1,versionName的第三位(patch)+1,并以versionName生成 tagandroid-v<version>
  2. 构建 APK:在仓库根目录依次执行yarn installyarn tscyarn buildParallel,然后在packages/app-mobile/android下运行./gradlew assembleRelease。源码注释特别说明不运行./gradlew clean,因为在 React Native 0.8x 上clean会触发 CMake 重新生成并因 autolinking/codegen 失败,改用yarn clean手动清理构建产物;
  3. 产物整理:APK 从android/app/build/outputs/apk/release/app-release.apk复制到packages/app-mobile/dist/joplin-v<version>.apk,主渠道(main)还会额外复制一份joplin-latest.apk
  4. 上传 GitHub Release:在joplin-android项目下创建对应 tag 的 release,并用 OAuth token 上传 APK;
  5. 写 changelog:调用completeReleaseWithChangelog把本次改动写入 android.md。

脚本还支持--release-name参数只构建指定渠道;源码中定义了customarmeabi-v7ax86arm64-v8ax86_64等多个ReleaseConfig,其中分架构构建目前处于disabled: true状态,当前实际启用的是maincustom两个渠道。

五、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

拆解为四步:

  1. git pull拉取最新代码;
  2. yarn buildParallel并行构建全部 workspace 包(底层是yarn workspaces foreach ... run build && yarn tsc);
  3. lerna version --yes --no-private --no-git-tag-version为所有非 private 的@joplin/*包提升 patch 版本号(不自动打 tag);
  4. 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 中把所有@joplindependenciesdevDependencies设置为新的 major/minor 版本(例如1.8,当前仓库实际值为~3.7):

"dependencies": { "@joplin/lib": "1.8", "@joplin/renderer": "1.8", "...": "..." }, "devDependencies": { "@joplin/tools": "1.8", "...": "..." }

最后发布 CLI 本体:

yarn releaseCli

release-cli.ts 的执行流程:git pullversionPatch()自增版本并生成 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 releaseServer

release-server.ts 的链路相对简洁:gitPullTry→ 进入packages/serverversionPatch()自增版本 → 生成 tagserver-<version>completeReleaseWithChangelog把 changelog 写入 server.md。tag 推送后由 CI 完成 Server 的实际构建(镜像构建相关脚本见 buildServerDocker.ts),本地命令同样只负责版本与 tag。

八、Web 剪藏器:releaseClipper

yarn releaseClipper

release-clipper.ts 的完整流程:

  1. 自增版本号:读取 manifest.json,把version的最后一位(build number)加一并写回;
  2. 构建扩展:切换util/joplinEnv.mjsprod模式(该文件被标记为// AUTOGENERATED by release-clipper),在packages/app-clipper/popupnpm run build
  3. 生成双平台发行包:分别组装dist/chromedist/firefox,两者的差异在manifest.json上——Chrome 包删除browser_specific_settings并去掉background.scripts/persistent(使用 MV3 service worker),Firefox 包则删除background.service_worker(使用 scripts 形式);随后各打成一个 zip;
  4. 源码包校验:额外打一个joplin-webclipper-source.zip(排除node_modules/build/dist/),用于浏览器商店的代码审查,并且脚本会在临时目录里重新npm install && npm run build,逐文件比对重新编译结果与已发布的 Firefox 包(MD5 对比),确保“发布的二进制与开源源码可复现一致”;
  5. 打 tag:若无--no-publish参数,则git commit+git tag clipper-<version>+ 推送;最后把joplinEnv.mjs切回dev模式。

九、插件生成器与插件仓库 CLI

插件生成器(generator-joplin)

按 DEPLOY.md,发布前一般先更新类型定义:

./updateTypes.sh

然后:

yarn releasePluginGenerator

release-plugin-generator.js 的自动化程度较高:进入packages/generator-joplin后,它会自己执行一次bash updateTypes.shversionPatch()自增版本,把package.jsonprivate字段临时去掉(setPackagePrivateField)以允许npm publishfinally中保证恢复为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,它的工作方式是:

  1. 执行node packages/tools/git-changelog <tag> --publish-format full从 git 提交记录生成该版本的变更清单(实现见 git-changelog.ts);
  2. 将结果以## <tag>加 UTC 时间戳的格式插入到对应 changelog 文件顶部(各产物对应 changelog 目录 下的cli.mdandroid.mdserver.md等文件);
  3. 打印提示:先用$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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询