news 2026/10/9 16:53:36

PhotoCraft 发布工程:从版本管理到多平台签名打包的完整发布流水线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PhotoCraft 发布工程:从版本管理到多平台签名打包的完整发布流水线
  • 图像处理
  • 桌面应用

【免费下载链接】photocraft

An open-source, clean-room reimplementation of Adobe Photoshop in pure Rust

项目地址:https://gitcode.com/gh_mirrors/pho/photocraft
点击查看免费下载

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:

  1. 提升版本号(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)。

  2. 把main合并(或快进)进release并推送。流水线自动开始。

  3. 等待草稿。大约 30 到 45 分钟(notarization 是最慢的一环)后,Releases 页会出现草稿PhotoCraft v0.2.0,tag 为推送 commit 上的v0.2.0,包含全部产物和SHA256SUMS.txt;release notes 由合并的 PR 自动生成。

  4. 检查。下载一两个安装包,查看 job summaries——任何::warning::都意味着某个签名秘密缺失,对应产物未签名。

  5. 补充 scorecard 增量。把自上一个版本以来 scorecard.md 的变化写进草稿 notes:对比两个 tag 的摘要表和性能表(git diff v<previous> v<this> -- docs/scorecard.md),逐项列出各领域的 done/partial/missing 变化、新达标或变慢的场景、提高的语料基线,以及"做了也没效果"的选项变化。引用数字时必须注明基线机器和负载平均值。

  6. 在 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.zipmacos-15
Windows 10+ x64photocraft-<v>-windows-x64.msi、photocraft-<v>-windows-x64-portable.zipwindows-latest
Windows 10+ x86(32 位)photocraft-<v>-windows-x86.msi、photocraft-<v>-windows-x86-portable.zipwindows-latest
Linux x86_64photocraft-<v>-linux-x86_64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak}ubuntu-22.04(Flatpak 为ubuntu-24.04)
Linux aarch64photocraft-<v>-linux-aarch64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak}ubuntu-22.04-arm(Flatpak 为ubuntu-24.04-arm)
FreeBSD 14 x86_64photocraft-<v>-freebsd-x86_64.tar.gzubuntu-latest上的 FreeBSD 14.3 VM
Webphotocraft-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-*.dmg

Windows:静态 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 时间戳。它按"存在哪种材料就用哪种"选择路径:

    1. .pfx文件(WINDOWS_CERTIFICATE、WINDOWS_CERTIFICATE_PASSWORD),由 DigiCert 时间戳;或
    2. 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_CERTIFICATEbase64.p12,"Developer ID Application: Learning Machines LLC (DJ6XS33FX8)"
APPLE_CERTIFICATE_PASSWORD上述.p12的密码
KEYCHAIN_PASSWORDCI 临时钥匙串的密码(未设置则随机)
APPLE_IDnotarytool使用的 Apple ID
APPLE_PASSWORD该 Apple ID 的应用专用密码
APPLE_TEAM_IDDJ6XS33FX8
WINDOWS_CERTIFICATEbase64.pfx代码签名证书
WINDOWS_CERTIFICATE_PASSWORD上述.pfx的密码
AZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_CLIENT_SECRETAzure Trusted Signing 服务主体(.pfx的替代方案)
AZURE_SIGNING_ENDPOINT如https://eus.codesigning.azure.net
AZURE_SIGNING_ACCOUNT、AZURE_CERT_PROFILETrusted 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

项目地址:https://gitcode.com/gh_mirrors/pho/photocraft
点击查看免费下载
上一篇:arduino-esp32 以太网实战指南:从 LAN8720/TLK110 RMII 到 W5500 SPI 的完整接入方案
下一篇:Django Ninja 表单参数(Form Data)解析与校验实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 16:46:32

ArcSDE 10.2 + Oracle 10g/11g:安装配置与高频排错指南

简介&#xff1a;面向Windows平台GIS管理员、开发人员及项目实施者&#xff0c;ArcSDE 10.2 for Oracle 10g/11g安装包用于解决ArcSDE中间件与Oracle数据库集成部署时的软件获取与安装准备问题。ArcSDE是ArcGIS核心组件&#xff0c;负责在关系型数据库中高效存储、访问和管理空…

作者头像 李华
网站建设 2026/10/9 16:44:54

MATLAB数据挖掘实战:从数据清洗到模型评估的完整教程

简介&#xff1a;面向工科生、数学专业及算法方向学习者&#xff0c;这套 MATLAB 数据分析与挖掘实战教程包含完整源码、说明文档与配套数据&#xff0c;覆盖数据探索、特征处理、模型构建和结果可视化等环节。代码采用参数化编程&#xff0c;关键参数便于修改&#xff0c;思路…

作者头像 李华
网站建设 2026/10/9 16:41:55

skynet游戏服务端源码解析:MySQL与Redis分工及避坑指南

简介&#xff1a;这份资源是面向游戏服务器开发者的完整源码包&#xff0c;基于轻量级高并发框架Skynet构建&#xff0c;重点解决游戏后端与MySQL、Redis两类数据库的协同交互问题。源码中实现了数据库访问层&#xff0c;将游戏逻辑产生的数据操作转换为SQL语句执行&#xff0c…

作者头像 李华
网站建设 2026/10/9 16:40:53

OA办公系统数据库设计:从RBAC权限到审批流的表结构全解析

简介&#xff1a;《OA办公系统大数据库设计.doc》是一份针对OA办公自动化管理系统的数据库设计说明书&#xff0c;面向系统设计、开发、验收、评审与测试人员。文档采用MSSQL SERVER 2008 R2&#xff0c;数据库名为OASYSDB/OA系统数据库&#xff0c;旨在将数据分析结果整理成计…

作者头像 李华
网站建设 2026/10/9 16:39:27

对偶生成对抗网络去雾实战:从天空翻车到PyTorch落地

简介&#xff1a;本资源为基于PyTorch实现的对偶生成对抗网络图像去雾项目&#xff0c;面向计算机相关专业正在做毕业设计的学生&#xff0c;以及需要项目实战练习的学习者&#xff0c;也可作为课程设计或期末大作业参考。项目包含完整Python源码、训练好的模型权重与文档说明&…

作者头像 李华
网站建设 2026/10/9 16:38:49

FPGA板卡硬件结构与升级原理全解析

1. FPGA板卡不是“黑盒子”&#xff0c;而是可编程的硬件积木场FPGA板卡这个词&#xff0c;最近在硬件开发、边缘计算和工业控制圈子被反复提起&#xff0c;但很多人一听到“FPGA”&#xff0c;第一反应还是“太硬核”“门槛高”“得会Verilog”——其实这是个典型的认知偏差。…

作者头像 李华