Skip to content

ci(release): sign and publish through the shared Colony workflow - #31

Merged
MotherSphere merged 6 commits into
mainfrom
ci/shared-signing
Oct 8, 2026
Merged

MotherSphere merged 6 commits into
mainfrom
ci/shared-signing

Conversation

@MotherSphere

Copy link
Copy Markdown
Member

What changes

Releases go through the shared signing workflow. release.yml now calls Project-Colony/Project-Colony-Resources/.github/workflows/sign-and-publish.yml, pinned to 619460ff4dc0049f129955f0988d368427b9eedb, after the build legs in the same run: preflight, Authenticode through SignPath (only once signpath-project-slug is set), the ed25519 .sig, .meta and .meta.sig over the final bytes, upload to the draft, download again, verify, publish. Authenticode rewrites the PE, so signing with the organisation key inside each build leg (the old flow) would describe bytes SignPath then replaces.

  • The build legs only build, check and upload artifacts (build-<asset>), and never see a key.
  • Kept: the Linux apt list, the colony.json validation, the --version smoke test, the x86_64 check on the cross-compiled macOS binary. Asset names are unchanged (grape-linux grape-windows.exe grape-macos grape-macos-x86, as on v0.4.1).
  • No build cache in the release legs (SignPath forbids reusing unverified build outputs), GitHub-hosted runners only, per-job permissions from an empty default, the checkout pins the tag and does not persist credentials.
  • A workflow_dispatch recovery path finishes a draft release whose run failed, started from the tag: gh workflow run release.yml --ref vX.Y.Z -f tag=vX.Y.Z.
  • signpath-project-slug stays commented out: SignPath has not approved the project yet. The two secrets are passed by name, never with inherit.

release-please reads its config again. The action was given release-type: rust next to config-file and manifest-file, and with release-type set, release-please-action v5 ignores both files. The input is gone, and "separate-pull-requests": false goes in the same change: with one named package it makes release-please use a branch without a component, and the merged release PR is then never tagged (what happened to SAM-Colony-Edition, fixed in its PR #6). The default for one package is what the v0.4.1 release PR already used (release-please--branches--main--components--grape). The manifest holds 0.4.1, the latest tag, and include-component-in-tag: false keeps tags as vX.Y.Z.

grape.exe carries a version resource. SignPath signs only an .exe whose ProductName is the project name and whose ProductVersion is set. A new build.rs writes one through winresource (no default features, so no TOML parser) when the target is Windows (CARGO_CFG_TARGET_OS, since build scripts run on the host): ProductName and FileDescription Grape, FileVersion and ProductVersion from CARGO_PKG_VERSION, which release-please bumps. Grape had no Windows resource before, so this is the only new build dependency. The Windows release leg fails right after the smoke test if grape-windows.exe does not carry ProductName Grape and the tag's version; ci.yml's Windows leg runs the same check against target\debug\grape.exe and the Cargo.toml version, so this PR proves it.

README: Code signing policy and Privacy policy. ## Code signing policy (anchor code-signing-policy, which the shared workflow links from every release) with the SignPath attribution, an honest line that Windows builds ship without Authenticode until SignPath accepts the project (every asset is always ed25519-signed and Colony verifies it), and the team roles. The privacy policy is written from the code: the only network call is Last.fm album.getInfo (src/library/metadata/online.rs:209-225), made only once the user has put an API key into preferences.json (online.rs:96-99 returns before any request when it is empty, and it is empty by default at src/config/mod.rs:755). It then fires when an album is selected (src/ui/app/update.rs:35-52, 630-632) unless the cache is fresh, and on the Enrich button (update.rs:241-243), sending the key, artist, album title and a Grape/<version> user agent. No telemetry, no update check (the update toggles in Preferences have no code behind them).

docs/internals/packaging.md now describes this flow instead of a sign job that no longer existed.

Checks run locally

  • cargo test: all suites pass (63 + 20 + 5 passed, ignored tests need audio hardware).
  • cargo clippy --all-targets -- -D warnings and cargo fmt --check fail, as on main: the known backlog ci.yml documents (419 clippy errors, all in src/; 42 files unformatted). Nothing in build.rs is flagged by either, including pedantic and nursery.
  • A throwaway crate with this exact build.rs, cross-built to x86_64-pc-windows-msvc from Linux (llvm-rc, lld-link), carries VERSIONINFO ProductName Grape, ProductVersion 0.4.1, FileDescription Grape, FileVersion 0.4.1. Grape itself was not cross-built locally.
  • Both workflows parse as YAML, the JSON files parse, Cargo.lock is accepted with --locked.

Only proven at the next release

  • The whole release path: draft hold, artifact upload, the call into sign-and-publish.yml, signing, verification on the draft, publishing.
  • The version resource check on the real release build of grape-windows.exe (the CI check covers the debug build on every PR).
  • release-please in manifest mode: the next release PR should come from release-please--branches--main--components--grape, and merging it should create vX.Y.Z.

Behaviour that changes with the config now applied

bump-minor-pre-major: true (a breaking change on 0.x bumps the minor, not to 1.0.0), the config's changelog sections (Fixes instead of Bug Fixes, refactor listed as Internals, docs listed), and the pull request header. All of it was already written in the config; it simply never applied.

The action was given `release-type: rust` together with `config-file` and
`manifest-file`. When `release-type` is set, release-please-action v5 ignores
both files, so the bump policy, the changelog sections and the pull request
header in release-please-config.json never applied.

Dropping the input turns the config on, so `"separate-pull-requests": false`
goes in the same change. With one named package, that setting makes
release-please open its pull request from a branch without a component, and
the merged pull request is then never tagged ("PR component: undefined does
not match configured component"). The default for one package is what the
v0.4.1 release pull request already used
(release-please--branches--main--components--grape).

The manifest already holds 0.4.1, the latest tag, and
`include-component-in-tag: false` keeps tags as vX.Y.Z.
SignPath signs only a Windows executable whose version resource names the
project (ProductName) and carries its version (ProductVersion). grape.exe had
no version resource at all.

build.rs now writes one through winresource when the target is Windows:
ProductName and FileDescription "Grape", FileVersion and ProductVersion from
CARGO_PKG_VERSION, which release-please bumps. The check is on
CARGO_CFG_TARGET_OS rather than cfg!(windows), since a build script runs on
the host. winresource is the only new build dependency, without its default
TOML feature, which build.rs does not use.

Other targets are unchanged: the build script does nothing for them.
Every Windows release now goes through
Project-Colony-Resources/.github/workflows/sign-and-publish.yml, pinned by
commit, in the same run as the build legs: preflight, Authenticode through
SignPath once a signpath-project-slug is set, the ed25519 .sig, .meta and
.meta.sig over the final bytes, upload to the draft, download again, verify,
publish. Authenticode rewrites the PE, so signing with the organisation key
inside each build leg, as before, would describe bytes SignPath then
replaces.

The build legs now only build, check and upload artifacts, and never see a
key. Kept from the old workflow: the Linux apt list, the colony.json
validation, the --version smoke test and the x86_64 check on the
cross-compiled macOS binary. Asset names are unchanged.

Also:
- a Windows step after the smoke test fails the leg if grape-windows.exe does
  not carry ProductName "Grape" and the tag's version as ProductVersion,
  which SignPath requires;
- no build cache in the release legs (SignPath forbids reusing unverified
  build outputs), GitHub-hosted runners only;
- permissions are per job, from an empty default;
- the checkout pins the tag and does not persist credentials;
- a workflow_dispatch recovery path finishes a draft release whose run
  failed, started from the tag.

signpath-project-slug stays commented out until SignPath has approved the
project. The two secrets are passed by name, not inherited.
The Windows test leg now reads target\debug\grape.exe's version resource
and fails unless ProductName is "Grape" and ProductVersion is the version in
Cargo.toml. The release workflow runs the same check on the release build;
this one proves on every pull request that build.rs still writes it.
SignPath Foundation requires a "Code signing policy" section on the home page,
and the shared release workflow links every release to
README#code-signing-policy. The section carries the SignPath attribution, says
honestly that Windows builds ship without Authenticode until the project is
accepted (every asset keeps its ed25519 signature, which Colony verifies), and
lists the team roles.

The privacy policy is written from the code, not from the feature list: the
only network call is Last.fm album.getInfo in
src/library/metadata/online.rs, made only once the user has put an API key
into preferences.json, and it sends the key, the album's artist and title and
a Grape/<version> user agent. Notifications, MPRIS, the Windows media
controls and the tray are local IPC.
The page still described a `sign` job that no longer existed and a config
that the action ignored. It now says what the release workflow does: build
legs that upload artifacts and never see a key, the Windows version resource
check, then the shared workflow that Authenticode-signs (once SignPath is on),
ed25519-signs the final bytes, verifies them on the draft and publishes. It
also gives the recovery command for a draft whose run failed.
@MotherSphere
MotherSphere merged commit 08fa91b into main Oct 8, 2026
8 checks passed
@MotherSphere
MotherSphere deleted the ci/shared-signing branch October 8, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant