资讯动态

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

发布时间:2026/10/9 16:53:40 来源:尧图企业网站定制
图像处理桌面应用【免费下载链接】photocraftAn 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 vversion。在维护者于 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.tomlCargo.lock。把main合并或快进进release并推送。流水线自动开始。等待草稿。大约 30 到 45 分钟notarization 是最慢的一环后Releases 页会出现草稿PhotoCraft v0.2.0tag 为推送 commit 上的v0.2.0包含全部产物和SHA256SUMS.txtrelease notes 由合并的 PR 自动生成。检查。下载一两个安装包查看 job summaries——任何::warning::都意味着某个签名秘密缺失对应产物未签名。补充 scorecard 增量。把自上一个版本以来 scorecard.md 的变化写进草稿 notes对比两个 tag 的摘要表和性能表git diff vprevious vthis -- 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严格要求 semverMAJOR.MINOR.PATCH 可选-pre标签无前置零01.0.0非法、不接受 build metadata1.0.0meta非法、连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_SHAgit 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 11universalApple silicon Intelphotocraft-v-macos-universal.dmg、photocraft-cli-v-macos-universal.zipmacos-15Windows 10 x64photocraft-v-windows-x64.msi、photocraft-v-windows-x64-portable.zipwindows-latestWindows 10 x8632 位photocraft-v-windows-x86.msi、photocraft-v-windows-x86-portable.zipwindows-latestLinux x86_64photocraft-v-linux-x86_64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak}ubuntu-22.04Flatpak 为ubuntu-24.04Linux aarch64photocraft-v-linux-aarch64.{AppImage,AppImage.zsync,deb,rpm,tar.gz,flatpak}ubuntu-22.04-armFlatpak 为ubuntu-24.04-armFreeBSD 14 x86_64photocraft-v-freebsd-x86_64.tar.gzubuntu-latest上的 FreeBSD 14.3 VMWebphotocraft-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_DIRCRAFT_FONTS_REQUIRED1构建使桌面发布内嵌其日文字体且缺失即失败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_TARGET11.0用lipo合并成 universal组装PhotoCraft.appInfo.plist由Info.plist.in生成bundle id 为ai.storyteller.photocraftplist 设置LSMinimumSystemVersion11.0、NSHighResolutionCapable和文档类型.pcraftOwnerPSD/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 放进 DMGhdiutil内含指向Applications的拖放链接DMG 本身也签名、公证、staple。脚本用codesign --verify --strict、stapler validate和spctl -a -vvv检查结果。CLIuniversal 的photocraft-cli用同一个 Developer ID hardened runtime 时间戳签名identifierai.storyteller.photocraft-clizip 后送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报告sourceNotarized 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 与 DMGpackaging/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-featurecrt-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-opWeb 构建也不碰该 crate。Release 构建使用 GUI 子系统开始菜单启动不弹控制台窗口。packaging/windows/photocraft.wxsWiX v5安装到 Program Files 的 per-machine 安装器带广告式advertised开始菜单快捷方式PhotoCraft 成为.pcraft的默认应用并出现在 PSD/PSB 与图片文件的打开方式中同时注册 App PathsWinR 输入photocraft可直接启动。MSI 版本号是纯数字X.Y.Z因为 MSI 没有预发布字段同版本升级被允许便于 release candidate 互相替换。快捷方式图标标识符保留.exe扩展名MSI 用标识符作为缓存图标文件名无扩展名可能渲染成空白文档图标。packaging/windows/check-icons.ps1 在 CI 中检查引用与扩展名package.ps1在签名前还会用 ICE50 校验 MSI。便携 zipphotocraft.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再签.msiSHA-256 RFC 3161 时间戳。它按存在哪种材料就用哪种选择路径.pfx文件WINDOWS_CERTIFICATE、WINDOWS_CERTIFICATE_PASSWORD由 DigiCert 时间戳或Azure Trusted SigningAZURE_*系列秘密脚本下载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 等工具据此只下载变化的块做增量更新。其AppRunpackaging/linux/AppRun在启动时做桌面集成Wayland 下 dock 图标来自合成器用窗口 app_id 解析已安装的.desktop文件——单靠 AppImage 永远提供不了这个否则每次运行都显示通用 Wayland 图标。该集成是尽力而为向~/.local/share/applications/安装ai.storyteller.photocraft.desktopExec改写为 AppImage 路径去掉TryExec因为二进制不在宿主 PATH 上并在旁边放 hicolor 图标仅在内容变化时重写任何失败都不影响启动。PHOTOCRAFT_NO_DESKTOP_INTEGRATION1可退出该行为脚本由 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 与其余格式装的是同一套二进制。流水线中每架构一个独立flatpakjobubuntu-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 回退、IPCX11 共享内存、driGPU、Pictures 与 Documents 的读写其余文件一律走文件选择器与 document 门户。无网络所以--control服务在沙箱内才可访问除非用户执行flatpak override --user --sharenetwork ai.storyteller.photocraft。宿主字体从/run/host/fonts和/run/host/user-fonts读取。本地Linux装好flatpak与flatpak-builderpackaging/linux/package.sh --formats tar packaging/linux/flatpak-bundle.sh。Flathub manifestpackaging/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.35Ubuntu 22.04、Debian 12、Fedora 36、RHEL 10、Tumbleweed。二进制直接链接的只有 glibc 和 libgcc_sX11、Wayland、xkbcommon、Vulkan、EGL 都在运行时从系统 dlopen 加载GPU 驱动也只能来自系统——这正是 .deb 和 .rpm 把它们声明为依赖完整清单及理由见 nfpm.yamlrpm 侧用 soname provides 以便 Fedora/RHEL 与 openSUSE 都能解析而 AppImage 不捆绑它们的原因Flatpak 则从 freedesktop 运行时获得。由于 AppImage 和 tarball 无法声明依赖photocraft在开窗前检查本会话X11 或 Wayland所需库apps/photocraft/src/linux_libs.rs缺失时打印应安装的包名并以状态码 1 退出而不是直接崩溃PHOTOCRAFT_SKIP_LIB_CHECK1可跳过检查。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虚拟机里出 tarballGitHub 没有 FreeBSD runner所以freebsdjob 用vmactions/freebsd-vm按 commit pin在 FreeBSD 14.3 VM 里跑 packaging/freebsd/package.sh安装与 freebsd.yml 相同的包外加bash。脚本以CARGO_BUILD_JOBS4构建两个二进制更多会耗尽 12 GB VM 的内存产出photocraft-v-freebsd-x86_64.tar.gz。FreeBSD 的uname -m是amd64文件名统一用x86_64。tarball 是/usr/local风格的树bin/photocraft、bin/photocraft-clishare/下是与 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 机器即可检查布局。Webtrunk 构建 24 MiB 体积门槛packaging/web/package.sh 执行trunk build --release配置见 apps/photocraft-web/Trunk.toml把dist/web连同示例_headers、.htaccess和部署指南打 zip。关键约束站点只使用相对 URLTrunk.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。Secretsrelease 环境里的签名材料所有秘密放在仓库的release环境Settings → Environments → release部署分支限制为release。release.yml 中每个签名或发布的 job 都声明environment: releaseFlatpak 与 FreeBSD job 不签名、不声明因此只有release分支的推送或手动运行能读到秘密。每个秘密都是可选的缺失时该平台产物未签名、运行中出现::warning::而不是构建失败。秘密用途APPLE_CERTIFICATEbase64.p12Developer ID Application: Learning Machines LLC (DJ6XS33FX8)APPLE_CERTIFICATE_PASSWORD上述.p12的密码KEYCHAIN_PASSWORDCI 临时钥匙串的密码未设置则随机APPLE_IDnotarytool使用的 Apple IDAPPLE_PASSWORD该 Apple ID 的应用专用密码APPLE_TEAM_IDDJ6XS33FX8WINDOWS_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.netAZURE_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_IDENTITYjob 结束时删除钥匙串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需要resvgmacOS 上另需iconutil。产物均已提交打包流程永远不需要这些工具。lint.github/workflows/packaging-lint.yml 在packaging/、工作流或图标有任何变更时数秒内跑完执行 actionlint、shellcheck、PowerShell 语法解析、xmllint、desktop-file-validate、appstreamcli validate以及一项 YAML 检查——确认两份 Flatpak manifestbundle 与 Flathub在运行时与权限上一致。小结这套发布工程的关键取舍单一版本来源[workspace.package] version一处管理cargo xtask version原子改写 离线刷新 lockCI 用xtask version set支持带版本覆盖的测试运行草稿即灰度构建产物先进草稿 Release可反复推送覆盖发布动作才创建 tag已发布版本会被流水线拒绝重复触碰秘密最小化与分支隔离release环境只允许release分支使用秘密可选、缺失降级为告警而非失败不签名的 job 干脆不接入环境产物自证macOS 有独立的verify.sh以 Gatekeeper 视角验证Linux 有产物自检步骤FreeBSD 无签名方案则以SHA256SUMS.txt兜底AppImage 内嵌 zsync 更新信息一份 FHS 树、多个格式Linux 全部格式同源制作Flatpak bundle 复用同一 tarball保证各格式二进制完全一致。赞分享图像处理桌面应用【免费下载链接】photocraftAn open-source, clean-room reimplementation of Adobe Photoshop in pure Rust项目地址https://gitcode.com/gh_mirrors/pho/photocraft点击查看免费下载相关推荐LightCraft 发布流水线解析从版本号到多平台签名产物的完整交付流程LightCraft 发布流水线解析从版本号到多平台签名产物的完整交付流程 LightCraft纯 Rust 实现的开源图像管理软件的发布流程由 GitHFilmCraft 发布流水线详解从单一版本号到多平台签名安装器的完整指南FilmCraft 发布流水线详解从单一版本号到多平台签名安装器的完整指南 本文基于仓库中的 docs/releasing.md https://link.gCCX 发布指南从版本号升级到多平台签名 Release 的完整流程CCX 发布指南从版本号升级到多平台签名 Release 的完整流程 CCXClaude / Codex / Gemini API Proxy的发布流程以API网关LLM 网关后端上一篇arduino-esp32 以太网实战指南从 LAN8720/TLK110 RMII 到 W5500 SPI 的完整接入方案下一篇Django Ninja 表单参数Form Data解析与校验实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑