- 图像处理
- 桌面应用
【免费下载链接】photocraft
An open-source, clean-room reimplementation of Adobe Photoshop in pure Rust
PhotoCraft(用纯 Rust 实现的 Photoshop 清洁室重实现)的发布不是"打个包传上去"这么简单:它由一条 GitHub Actions 流水线统一构建 macOS、Windows、Linux、FreeBSD 与 Web 五个平台的签名产物,自动创建草稿 Release,再由维护者检查后发布。本文完整梳理 PhotoCraft 的发布流程——版本号如何单一来源管理、各平台产物如何构建与签名、秘密(secrets)如何隔离、以及维护者发布前需要执行的每一步操作——读完你可以完整复现一次 PhotoCraft 版本发布,并理解其打包脚本背后的工程取舍。
发布总览:推送即构建,草稿即隔离
PhotoCraft 的核心发布机制是一条约定:向release分支推送代码,就会触发release.yml 流水线,构建所有平台的签名安装包,然后创建或更新一个**草稿(draft)**GitHub Release,命名为PhotoCraft v<version>。在维护者于 GitHub UI 中点击"发布"之前,任何人都看不到这个草稿。
这条流水线是共享发布手册(release playbook)在 PhotoCraft 中的参考实现,相关文件为 release-playbook.md、release.yml 与 releasing.md 本身。对外名称统一用PhotoCraft,而文件名、二进制名和 id 保持小写:photocraft-<version>-<platform>-<arch>.<ext>、ai.storyteller.photocraft。
从流水线源码可以看到几个关键工程细节:
- 并发控制:
concurrency组为release-${{ github.ref }}且cancel-in-progress: false——注释明确说明"绝不在 notarization 或上传中途取消运行",新推送只能排队等待。 - 版本校验前置:
version任务先用正则^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-...)?$校验版本号格式,判断是否带预发布后缀(如-rc.1),并把version、date、prerelease三个输出传递给后续所有任务。 - 环境隔离:所有会签名或发布的 job 都声明
environment: release,而该环境被限制为仅release分支可用——只有推送该分支或在分支上手动运行,才能读到签名秘密。 - 发布保护:最终
release任务在上传前会检查同名 tag:如果 Release 已非草稿(即已发布),直接报错Release v... is already published. Bump the version before pushing to release。因此在草稿发布后再次推送同一版本会被拒绝,必须先升版本号。
在草稿发布前再次推送release分支,流水线会重建同一个草稿并替换其全部资产(先删除旧 asset 再gh release upload --clobber),这也是"发布前可以反复修"的基础。
切割一次发布的六步操作
这是维护者视角的标准操作流程,完整继承自 releasing.md:
提升版本号(Bump the version)。版本号在仓库中只有一处:根 Cargo.toml 的
[workspace.package] version(当前为0.3.0)。所有 crate 通过version.workspace = true继承它:cargo xtask version # 打印当前版本,如 0.1.0 cargo xtask version set 0.2.0 # 或 0.2.0-rc.1;同步更新 Cargo.toml 和 Cargo.lock然后通过正常评审流程提交改动(
Cargo.toml+Cargo.lock)。把
main合并(或快进)进release并推送。流水线自动开始。等待草稿。大约 30 到 45 分钟(notarization 是最慢的一环)后,Releases 页会出现草稿
PhotoCraft v0.2.0,tag 为推送 commit 上的v0.2.0,包含全部产物和SHA256SUMS.txt;release notes 由合并的 PR 自动生成。检查。下载一两个安装包,查看 job summaries——任何
::warning::都意味着某个签名秘密缺失,对应产物未签名。补充 scorecard 增量。把自上一个版本以来 scorecard.md 的变化写进草稿 notes:对比两个 tag 的摘要表和性能表(
git diff v<previous> v<this> -- docs/scorecard.md),逐项列出各领域的 done/partial/missing 变化、新达标或变慢的场景、提高的语料基线,以及"做了也没效果"的选项变化。引用数字时必须注明基线机器和负载平均值。在 GitHub UI 发布草稿。发布会创建
v0.2.0tag;带预发布后缀(-rc.1)的版本会标记为 pre-release。
版本号的单一来源:cargo xtask version
版本号为什么只放在一处?xtask/src/version.rs 给出了实现答案:
read只识别[workspace.package]段内的version = "…"行,拒绝[package]段下的版本;replace只替换那一行,其余内容逐字节不变(保留 CRLF 行尾),并且原子写入——先写到target/Cargo.toml.xtask-version再 rename,注释解释"半写坏的根 manifest 会打断整个 workspace 的构建";- 写完后执行
cargo update --workspace --offline刷新Cargo.lock中 workspace 成员的版本(优先离线,失败才联网); validate严格要求 semver(MAJOR.MINOR.PATCH+ 可选-pre标签):无前置零(01.0.0非法)、不接受 build metadata(1.0.0+meta非法)、连v1.0.0都拒绝。测试覆盖了正反两类用例。
CI 侧则把同一套逻辑接到手动触发上:workflow_dispatch提供可选version输入,各构建 job 在构建前执行cargo xtask version set "$PHOTOCRAFT_VERSION",因此测试运行产出的二进制--version也会报告覆盖后的版本。注意测试运行仍需release环境,所以手动运行时要选择release分支。
版本号如何进入二进制:build_info
每个二进制(photocraft --version、photocraft-cli --version、Help › About PhotoCraft)都报告版本、commit 和构建日期。其实现是 crates/engine/src/build_info.rs,用option_env!在编译期读取两个环境变量:
PHOTOCRAFT_BUILD_SHA:git commit(显示时截短到 9 字符);PHOTOCRAFT_BUILD_DATE:构建日期,约定YYYY-MM-DD。
long_version()的格式是0.1.0 (3f2a9c1d0, 2026-10-01);两者都没有时报告0.1.0 (dev build)。模块注释特意强调"这里不 shell 调 git",所以源码 tarball 构建和 wasm 构建都正常工作。release.yml 在顶层env:中把PHOTOCRAFT_BUILD_SHA设为github.sha;本地打包则由 packaging/env.sh 负责——它同时导出VERSION(可被PHOTOCRAFT_VERSION覆盖)、DIST(默认$ROOT/dist/release)、PHOTOCRAFT_BUILD_SHA(缺失时取git rev-parse HEAD)和PHOTOCRAFT_BUILD_DATE(默认 UTC 当日)。
各平台构建了什么
| 平台 | 产物 | 构建环境 |
|---|---|---|
| macOS 11+(universal:Apple silicon + Intel) | photocraft-<v>-macos-universal.dmg、photocraft-cli-<v>-macos-universal.zip | macos-15 |
| Windows 10+ x64 | photocraft-<v>-windows-x64.msi、photocraft-<v>-windows-x64-portable.zip | windows-latest |
| Windows 10+ x86(32 位) | photocraft-<v>-windows-x86.msi、photocraft-<v>-windows-x86-portable.zip | windows-latest |
| Linux x86_64 | photocraft-<v>-linux-x86_64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak} | ubuntu-22.04(Flatpak 为ubuntu-24.04) |
| Linux aarch64 | photocraft-<v>-linux-aarch64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak} | ubuntu-22.04-arm(Flatpak 为ubuntu-24.04-arm) |
| FreeBSD 14 x86_64 | photocraft-<v>-freebsd-x86_64.tar.gz | ubuntu-latest上的 FreeBSD 14.3 VM |
| Web | photocraft-web-<v>.zip(静态站点,见 Web 打包说明) | ubuntu-latest |
两个值得注意的补充:Windows 任务矩阵实际还包含arm64(在 x64 runner 上交叉编译aarch64-pc-windows-msvc,由 windows-arm64.yml 在真机上验证安装运行);另外,除 Web 外的每个桌面构建 job 都会 checkout 字体仓库 craft-fonts 到 release.yml 顶部CRAFT_FONTS_REF固定的 commit,并以CRAFT_FONTS_DIR+CRAFT_FONTS_REQUIRED=1构建,使桌面发布内嵌其日文字体且缺失即失败(Web 构建不内嵌,原因见下文 Web 小节的 24 MiB 门槛)。每个字体的许可证随包携带为OFL-<family>.txt,由 packaging/env.sh 的copy_font_licences拷贝(Windows portable zip 和 macOS 的Contents/Resources/Licenses)。字体 pin 要有意地、与ci.yml中那处一起提升。
macOS:签名、公证与验证
packaging/macos/package.sh构建aarch64-apple-darwin和x86_64-apple-darwin两个目标(MACOSX_DEPLOYMENT_TARGET=11.0),用lipo合并成 universal,组装PhotoCraft.app:
- Info.plist由
Info.plist.in生成,bundle id 为ai.storyteller.photocraft;plist 设置LSMinimumSystemVersion11.0、NSHighResolutionCapable和文档类型:.pcraft(Owner),PSD/PSB 与 PhotoCraft 可读的图片格式标为 Alternate(不抢占 Preview 的默认关联)。图标是 assets/app-icon/photocraft.icns。 - 签名自内向外(inside-out),带 hardened runtime 和安全时间戳:先签可执行文件,再签 bundle,最终签名没有
--deep。entitlements.plist刻意留空。 - 公证:app 打成 zip 后
xcrun notarytool submit --wait,再把 ticket staple 到 app 上;app 放进 DMG(hdiutil,内含指向Applications的拖放链接),DMG 本身也签名、公证、staple。脚本用codesign --verify --strict、stapler validate和spctl -a -vvv检查结果。 - CLI:universal 的
photocraft-cli用同一个 Developer ID + hardened runtime + 时间戳签名(identifierai.storyteller.photocraft-cli),zip 后送notarytool。由于只有.app、.dmg、.pkg能携带 stapled ticket,裸 Mach-O 不行——Gatekeeper 会在首次运行被隔离(quarantined)的 CLI 时在线查询票据;离线时首次运行可能被拒,直到 Mac 重新联网。 - 验证:packaging/macos/verify.sh 以"用户下载到的样子"检查产物:解包 CLI zip,要求内部二进制通过
codesign --verify --strict、双架构齐全、hardened runtime 标志、来自APPLE_TEAM_ID的Developer ID Application授权链、时间戳,且spctl --assess --type install报告source=Notarized Developer ID(即 Gatekeeper 做的那次在线查询);对 DMG 则跑codesign --verify、stapler validate、spctl --type open。release 流水线在打包之后单独跑这一步:有签名秘密时任何缺失都让 job 失败,没有秘密时只查签名完整性并告警。本地构建后也可运行:packaging/macos/verify.sh --arch aarch64。
本地无证书开发时,脚本会 ad-hoc 签名(codesign -s -)并跳过公证,足以在自己的 Mac 上验证 bundle 与 DMG:
packaging/macos/package.sh # universal;需要两个 rustup 目标 packaging/macos/package.sh --arch aarch64 # 更快,仅宿主机目标 open dist/release/photocraft-*-macos-*.dmgWindows:静态 CRT、WiX 安装器与便携模式
packaging/windows/package.ps1 -Arch x64|x86以-C target-feature=+crt-static构建。静态 C 运行时意味着 MSI 和 portable zip 都不依赖 Visual C++ 运行库——对独立安装器很重要,代价只有约 100 KB。该 flag 放在CARGO_TARGET_<TRIPLE>_RUSTFLAGS中,不影响 host 构建脚本。
apps/photocraft/build.rs 仅在目标为 Windows 时用
winresourcecrate 内嵌图标(assets/app-icon/photocraft.ico)和 VERSIONINFO;其他平台是 no-op,Web 构建也不碰该 crate。Release 构建使用 GUI 子系统,开始菜单启动不弹控制台窗口。
packaging/windows/photocraft.wxs(WiX v5)安装到 Program Files 的 per-machine 安装器,带广告式(advertised)开始菜单快捷方式;PhotoCraft 成为
.pcraft的默认应用,并出现在 PSD/PSB 与图片文件的"打开方式"中;同时注册 App Paths(Win+R 输入photocraft可直接启动)。MSI 版本号是纯数字X.Y.Z,因为 MSI 没有预发布字段;同版本升级被允许,便于 release candidate 互相替换。快捷方式图标标识符保留
.exe扩展名:MSI 用标识符作为缓存图标文件名,无扩展名可能渲染成空白文档图标。packaging/windows/check-icons.ps1 在 CI 中检查引用与扩展名;package.ps1在签名前还会用 ICE50 校验 MSI。便携 zip:
photocraft.exe旁附带 packaging/windows/portable.txt。该标记文件(或PhotoCraft.portable)开启便携模式:偏好设置、预设、崩溃恢复自动保存和 GPU 启动标记都写入 exe 旁的PhotoCraftData\,而不是%APPDATA%\Photocraft;该目录不可写时应用告警并回退到%APPDATA%。MSI 不带此标记。逻辑实现在 apps/photocraft/src/app_dirs.rs,在 macOS 和 Linux 上同样工作(该模块还优先识别PHOTOCRAFT_CONFIG_DIR覆盖,供测试与代理使用)。签名:packaging/windows/sign.ps1 用
signtool先签两个.exe再签.msi,SHA-256 + RFC 3161 时间戳。它按"存在哪种材料就用哪种"选择路径:.pfx文件(WINDOWS_CERTIFICATE、WINDOWS_CERTIFICATE_PASSWORD),由 DigiCert 时间戳;或- Azure Trusted Signing(
AZURE_*系列秘密):脚本下载Microsoft.Trusted.Signing.Clientdlib,用微软服务器做时间戳。
Windows 签名方案变化时只需改这一个脚本。
本地构建:dotnet tool install -g wix --version 5.0.2,然后pwsh packaging/windows/package.ps1 -Arch x64。
Linux:一棵 FHS 树产出全部格式
packaging/linux/package.sh先搭好一棵 FHS 树,所有格式都从这棵树制作。树中包含两个二进制、ai.storyteller.photocraft.desktop、16–512 px 的 hicolor 图标加可缩放 SVG、AppStream metainfo,以及为.pcraft、.psb、.qoi注册的 shared-mime-info 文件。各格式的选择理由:
AppImage:任何发行版免安装即跑,是"下载即用"选项和包未覆盖发行版的兜底。用仍在维护的
AppImage/appimagetool构建,其静态运行时不依赖 libfuse2。每个 AppImage 内嵌更新信息(gh-releases-zsync|…|latest|…),旁边附.zsync文件,AppImageUpdate、AppImageLauncher 等工具据此只下载变化的块做增量更新。其AppRun(packaging/linux/AppRun)在启动时做桌面集成:Wayland 下 dock 图标来自合成器用窗口 app_id 解析已安装的.desktop文件——单靠 AppImage 永远提供不了这个,否则每次运行都显示通用 Wayland 图标。该集成是尽力而为:向~/.local/share/applications/安装ai.storyteller.photocraft.desktop(Exec改写为 AppImage 路径,去掉TryExec,因为二进制不在宿主 PATH 上)并在旁边放 hicolor 图标,仅在内容变化时重写,任何失败都不影响启动。PHOTOCRAFT_NO_DESKTOP_INTEGRATION=1可退出该行为;脚本由 packaging/linux/apprun-test.sh 在 packaging-lint 工作流中覆盖。.deb / .rpm:分别覆盖 Debian/Ubuntu/Mint/Pop!_OS/elementary 和 Fedora/RHEL/openSUSE。两者都集成菜单、MIME 和图标缓存(packaging/linux/postinst.sh 钩子)并可干净卸载。两者都由 nfpm 构建——一个受维护、零依赖的工具加一份配置(packaging/linux/nfpm.yaml),省去在每个 crate 里维护 cargo-deb / cargo-generate-rpm 元数据,且能在 Ubuntu 上同时产出两种格式。
.tar.gz:给自管
/opt或~/.local的用户。Flatpak bundle:单文件安装进 Flatpak,沙箱化,通过安装新 bundle 升级。packaging/linux/flatpak-bundle.sh 把 job 产出的
.tar.gz用 packaging/linux/flatpak/ai.storyteller.photocraft.bundle.yml 在 freedesktop 26.08 运行时上重新打包——不做 Rust 构建、flatpak-builder内不需要网络,因此 bundle 与其余格式装的是同一套二进制。流水线中每架构一个独立flatpakjob(ubuntu-24.04与ubuntu-24.04-arm,为 flatpak-builder 1.4),下载 Linux job 的产物、跑脚本、安装 bundle 并在沙箱里执行photocraft-cli --version冒烟测试。bundle 把 Flathub 声明为运行时源,用户安装方式:flatpak install --user photocraft-<v>-linux-x86_64.flatpak # 缺运行时则从 Flathub 拉 org.freedesktop.Platform//26.08 flatpak run ai.storyteller.photocraft沙箱权限(在 manifest 中给出理由):Wayland 带 X11 回退、IPC(X11 共享内存)、
dri(GPU)、Pictures 与 Documents 的读写;其余文件一律走文件选择器与 document 门户。无网络,所以--control服务在沙箱内才可访问,除非用户执行flatpak override --user --share=network ai.storyteller.photocraft。宿主字体从/run/host/fonts和/run/host/user-fonts读取。本地(Linux,装好flatpak与flatpak-builder):packaging/linux/package.sh --formats tar && packaging/linux/flatpak-bundle.sh。Flathub manifest:packaging/linux/flatpak/ai.storyteller.photocraft.yml 从源码构建,已具备提交 Flathub 的条件(与 bundle manifest 保持相同运行时和
finish-args,由 packaging-lint 校验),但 CI 不构建它——真实构建需要 vendored crate 源码(flatpak-cargo-generator.py生成的cargo-sources.json),每架构额外 20 分钟以上,须按 manifest 头部注释的命令手动构建。
glibc 基线:所有 Linux 二进制在 Ubuntu 22.04 上构建,要求glibc ≥ 2.35(Ubuntu 22.04+、Debian 12+、Fedora 36+、RHEL 10、Tumbleweed)。二进制直接链接的只有 glibc 和 libgcc_s;X11、Wayland、xkbcommon、Vulkan、EGL 都在运行时从系统 dlopen 加载,GPU 驱动也只能来自系统——这正是 .deb 和 .rpm 把它们声明为依赖(完整清单及理由见 nfpm.yaml,rpm 侧用 soname provides 以便 Fedora/RHEL 与 openSUSE 都能解析)而 AppImage 不捆绑它们的原因;Flatpak 则从 freedesktop 运行时获得。由于 AppImage 和 tarball 无法声明依赖,photocraft在开窗前检查本会话(X11 或 Wayland)所需库(apps/photocraft/src/linux_libs.rs),缺失时打印应安装的包名并以状态码 1 退出,而不是直接崩溃;PHOTOCRAFT_SKIP_LIB_CHECK=1可跳过检查。CI 的 linux job 还会跑一组产物自检:dpkg-deb --info/--contents、ldd、AppImage--version与--appimage-updateinformation里gh-releases-zsync|的存在性、.zsync非空等。
本地构建:先安装 nfpm,然后packaging/linux/package.sh(或--formats "deb tar")。
FreeBSD:虚拟机里出 tarball
GitHub 没有 FreeBSD runner,所以freebsdjob 用vmactions/freebsd-vm(按 commit pin)在 FreeBSD 14.3 VM 里跑 packaging/freebsd/package.sh,安装与 freebsd.yml 相同的包外加bash。脚本以CARGO_BUILD_JOBS=4构建两个二进制(更多会耗尽 12 GB VM 的内存),产出photocraft-<v>-freebsd-x86_64.tar.gz。FreeBSD 的uname -m是amd64,文件名统一用x86_64。tarball 是/usr/local风格的树:bin/photocraft、bin/photocraft-cli,share/下是与 Linux 相同的 desktop entry、MIME 类型、AppStream metainfo、hicolor 图标,外加share/doc/photocraft/里的许可证、NOTICE和ATTRIBUTION.md。用户安装方式:
pkg install libxkbcommon wayland libX11 libXcursor libXrandr libXi libxcb mesa-libs vulkan-loader gtk3 fontconfig freetype2 alsa-lib tar -xzf photocraft-<v>-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local安全细节:该 job 不签名,因此不声明environment: release,拿不到任何秘密;版本、构建日期、commit 通过 action 的envs:按名字传入 VM(绝不模板化进脚本),构建发生在/var/tmp,只有dist/被拷出 VM。由于 FreeBSD 对散装二进制没有代码签名机制,二进制不签名——用户请对照SHA256SUMS.txt校验 tarball。另外 FreeBSD CI 工作流在main推送和触及packaging/freebsd/、freebsd.yml、release.yml的 PR 上也会跑同一脚本,所以发布 job 不是它第一次运行。任何系统上都可以用packaging/freebsd/package.sh --dry-run从 stub 二进制搭树并列出 tarball,无需 FreeBSD 机器即可检查布局。
Web:trunk 构建 + 24 MiB 体积门槛
packaging/web/package.sh 执行trunk build --release(配置见 apps/photocraft-web/Trunk.toml),把dist/web连同示例_headers、.htaccess和部署指南打 zip。关键约束:
- 站点只使用相对 URL(
Trunk.toml中public_url = "./"),因此可以挂在任意路径下、放进 iframe——脚本还会grep检查index.html中不存在根绝对路径src/href="/...",发现即失败。 - wasm 使用体积优化的
wasm-releaseCargo profile(在 apps/photocraft-web/index.html 中指定),脚本对任何超过24 MiB的.wasm直接失败——低于 Cloudflare 的单文件 25 MiB 上限,"宁可在这里失败,也不在上传时失败"。这也是 Web 构建不内嵌 craft-fonts 的原因:哪怕一个日文字体就会让 wasm 越过这道门槛。 - MIME 类型、压缩、缓存、iframe 片段和
?webgl/?cpu参数见 packaging/web/README.md。
Secrets:release 环境里的签名材料
所有秘密放在仓库的release环境(Settings → Environments → release),部署分支限制为release。release.yml 中每个签名或发布的 job 都声明environment: release(Flatpak 与 FreeBSD job 不签名、不声明),因此只有release分支的推送或手动运行能读到秘密。每个秘密都是可选的:缺失时该平台产物未签名、运行中出现::warning::,而不是构建失败。
| 秘密 | 用途 |
|---|---|
APPLE_CERTIFICATE | base64.p12,"Developer ID Application: Learning Machines LLC (DJ6XS33FX8)" |
APPLE_CERTIFICATE_PASSWORD | 上述.p12的密码 |
KEYCHAIN_PASSWORD | CI 临时钥匙串的密码(未设置则随机) |
APPLE_ID | notarytool使用的 Apple ID |
APPLE_PASSWORD | 该 Apple ID 的应用专用密码 |
APPLE_TEAM_ID | DJ6XS33FX8 |
WINDOWS_CERTIFICATE | base64.pfx代码签名证书 |
WINDOWS_CERTIFICATE_PASSWORD | 上述.pfx的密码 |
AZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_CLIENT_SECRET | Azure Trusted Signing 服务主体(.pfx的替代方案) |
AZURE_SIGNING_ENDPOINT | 如https://eus.codesigning.azure.net |
AZURE_SIGNING_ACCOUNT、AZURE_CERT_PROFILE | Trusted Signing 账户与证书配置文件名 |
GITHUB_TOKEN负责创建 Release,且只有最终 job 被授予contents: write(全局权限是contents: read)。
macOS 侧,packaging/macos/import-cert.sh 把.p12解码进临时钥匙串:security create-keychain→security import→set-key-partition-list(使 codesign 无需提示即可用密钥),并把身份 SHA-1 导出为MACOS_SIGN_IDENTITY;job 结束时删除钥匙串(release.yml 中有if: always()的兜底清理步骤)。
图标与 lint 检查
图标:assets/app-icon/photocraft.svg 是权威图标(九尾狐插画,MIT OR Apache-2.0,见该目录 LICENSE.txt 与 README.md)。packaging/icons.sh 从它再生成 1024 px PNG、.icns、.ico(经cargo xtask ico打包)和各尺寸 hicolor PNG,需要resvg,macOS 上另需iconutil。产物均已提交,打包流程永远不需要这些工具。
lint:.github/workflows/packaging-lint.yml 在packaging/、工作流或图标有任何变更时数秒内跑完,执行 actionlint、shellcheck、PowerShell 语法解析、xmllint、desktop-file-validate、appstreamcli validate,以及一项 YAML 检查——确认两份 Flatpak manifest(bundle 与 Flathub)在运行时与权限上一致。
小结:这套发布工程的关键取舍
- 单一版本来源:
[workspace.package] version一处管理,cargo xtask version原子改写 + 离线刷新 lock,CI 用xtask version set支持带版本覆盖的测试运行; - 草稿即灰度:构建产物先进草稿 Release,可反复推送覆盖,发布动作才创建 tag,已发布版本会被流水线拒绝重复触碰;
- 秘密最小化与分支隔离:
release环境只允许release分支使用,秘密可选、缺失降级为告警而非失败,不签名的 job 干脆不接入环境; - 产物自证:macOS 有独立的
verify.sh以 Gatekeeper 视角验证,Linux 有产物自检步骤,FreeBSD 无签名方案则以SHA256SUMS.txt兜底,AppImage 内嵌 zsync 更新信息; - 一份 FHS 树、多个格式:Linux 全部格式同源制作,Flatpak bundle 复用同一 tarball,保证各格式二进制完全一致。
- 图像处理
- 桌面应用
【免费下载链接】photocraft
An open-source, clean-room reimplementation of Adobe Photoshop in pure Rust
相关推荐
LightCraft 发布流水线解析:从版本号到多平台签名产物的完整交付流程
LightCraft 发布流水线解析:从版本号到多平台签名产物的完整交付流程 LightCraft(纯 Rust 实现的开源图像管理软件)的发布流程由 GitH
FilmCraft 发布流水线详解:从单一版本号到多平台签名安装器的完整指南
FilmCraft 发布流水线详解:从单一版本号到多平台签名安装器的完整指南 本文基于仓库中的 docs/releasing.md https://link.g
CCX 发布指南:从版本号升级到多平台签名 Release 的完整流程
CCX 发布指南:从版本号升级到多平台签名 Release 的完整流程 CCX(Claude / Codex / Gemini API Proxy)的发布流程以
API网关LLM 网关后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考