feat(release): application release command family - #336
Open
JulianRuiseco wants to merge 2 commits into
Open
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
JulianRuiseco
force-pushed
the
jrr/release-commands
branch
from
August 18, 2026 15:31
6629c4c to
bd8ed3d
Compare
The release contract logic moves here from fsl_libs' .github/scripts (bash), per the doctrine that fslabscli owns CI logic; the fsl_libs workflows become thin invocations of a pinned fslabscli (fslabs/fsl_libs#4230). This also makes fslabscli the single owner of the library-vs-application release boundary: `release classify` shares the publish- tag-prefix constant with the publish path, so a library release this tool creates is skipped by the app pipeline by construction. This PR is the pipeline CORE - everything a release cannot ship without; the operational periphery (resolve, record, healthcheck, cleanup-drafts) stacks on top in a follow-up PR. - classify: library skip vs validated <app>-<version> app release, keyed on the TAG (this tool leaves library release names null); apps are discovered from [package.metadata.fslabs.release] across every git-tracked manifest, INCLUDING excluded workspaces (fdk_apps holds the only shipped app). - verify-production: revision resolved from the tag itself (never target_commitish, which is a branch name), ancestry of main, workspace version binding, green check runs. - probe-store: the conditional-write go/no-go gate against a deployed store. Object lock plus versioning stops deletion but NOT overwrites; only conditional writes make published objects immutable. - publish: completeness against configured targets (missing OR extra artifacts fail; every linux artifact needs its .asc), signature re-verification against the bytes (osslsigncode per MSI, gpg against the PUBLISHED key for linux; DMG is stapler-validated on the mac host), digesting, monotonicity derived from COMMITTED MANIFESTS (never index.json), artifact writes read back and digest-compared, the manifest written LAST as the atomic commit point, index compare-and-swap. A refused overwrite propagates as-is: the version is spent. - promote: the only writer of channels.json. Production-manifest gate ("previews never"), per-target artifact presence, selected-artifact digest re-verification, and a pure apply_move whose backward gate re-runs INSIDE the CAS retry loop; acknowledged backward moves (the rollback procedure) record backward:true plus the reason in a capped provenance log. - bundle-linux: cargo-deb plus a bundle-nothing AppImage (pinned appimagetool by sha256) with the glibc floor asserted from the binary's versioned symbols; no versioned glibc symbols at all is an error, not a pass. - sign-linux: temp GNUPGHOME, detach-sign, self-verify, fingerprint out. types.rs is the single source of truth for the manifest / index / channels.json shapes; store.rs holds the storage semantics on the existing opendal dependency; http.rs and keyring.rs are the one HTTP client and one gpg keyring every command shares. New deps: semver, thiserror. All commands run on Linux runners, so the existing linux-musl release asset remains the only distributed binary. Proven end to end with the built binary against a throwaway MinIO (publish 4 artifacts + manifest-last; re-publish refused; backward move refused then acknowledged with provenance) and against the real fsl_libs tree (classify discovers spatial_engine from metadata in the excluded workspace). Integration defects found by those live runs, invisible to the unit tests: toml 0.9 document-vs-value parsing, clap's propagated auto --version flag colliding with --version arguments, and opendal expressing S3 If-None-Match:* as if_not_exists. Known wart: main.rs requires a git root before dispatch; a follow-up. The legacy publish.binary installer/channel model in check_workspace/binary.rs is superseded by this family; deprecation is follow-up after the fsl_libs cutover. cargo test: 311 passed. clippy: zero diagnostics.
JulianRuiseco
force-pushed
the
jrr/release-commands
branch
from
August 18, 2026 15:37
bd8ed3d to
7d71b5c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
fslabscli is the de facto location for CI logic. This PR gives it the
application release pipeline: the logic behind fsl_libs' release workflows
(fslabs/fsl_libs#4230 rewrites them as thin, pinned invocations of these
commands), which previously lived as bash under that repo's
.github/scripts/.It also makes fslabscli the single owner of the library-vs-application
release boundary:
release classifyshares thepublish-tag-prefixconstant with the publish path, so a library release created by
fslabscli publishis skipped by the app pipeline by construction.Solution
This PR is the pipeline CORE - everything a release cannot ship without.
The operational periphery (
resolve,record,healthcheck,cleanup-drafts) stacks on top in a follow-up PR so each unit staysreviewable.
A
releasesubcommand family (cargo fslabscli release <cmd>):classify<app>-<version>app release; app discovery from[package.metadata.fslabs.release]across every git-tracked manifest, including excluded workspacesverify-productiontarget_commitish), ancestry of main, workspace version binding, green check runsprobe-storepublishpromotebundle-linuxsign-linuxThe manifest / index / channels.json serde definitions in
release/types.rsare the single source of truth for the published objectshapes; storage semantics live in
release/store.rson the existingopendal dependency (put-if-absent via
if_not_exists, ETag If-Match CASwith the transform re-run on every retry). New deps:
semver,thiserror. All commands run on Linux runners, so the existinglinux-musl release asset remains the only distributed binary.
This family supersedes the legacy
publish.binaryinstaller/channel modelin
check_workspace/binary.rs(nightly/alpha blob channels); deprecatingand removing that is follow-up work after the fsl_libs cutover.
Size
One squashed commit, ~3.4k lines: seven subcommands, their 45 unit tests
(the ported specification of the bash this replaces), and the shared
contract types / storage / HTTP / keyring modules. The doc comments carry
the published contract (why the manifest is written last, why
monotonicity ignores index.json, why a backward move re-validates inside
the CAS loop) and are load-bearing. Review path: types.rs + store.rs
first (the contract), then classify/publish/promote (the decisions), then
bundle/sign/probe (tool wrappers). The stacked periphery PR is ~1.3k.
Testing
cargo test: 324 passed (58 in the release module: classificationgrammar and tag-boundary scenarios, conditional-write semantics, the
backward-move gate and provenance cap, manifest completeness both
directions, glibc symbol parsing, api-url normalisation).
sign-linux -> publish 1.0.0 and 1.1.0 (six objects each, manifest last;
re-publishing 1.0.0 refused with the version-spent error) -> promote
latest -> unacknowledged backward move refused, acknowledged one records
backward: true+ reason -> resolve--downloaddigest- andGPG-verifies against the published key -> healthcheck reports the
surface healthy, and names both digests when an object is tampered.
release classifydiscoversspatial_engine from its metadata in the excluded fdk_apps workspace,
routes production/preview correctly, and skips
publish-*releasesincluding null-name ones.
invisible to unit tests): toml 0.9 document-vs-value parsing, clap's
propagated auto
--versionflag colliding with--versionarguments,and opendal expressing S3 If-None-Match:* as
if_not_exists.Known wart:
main.rsrequires a git root before dispatch, sorelease resolvepanics outside a checkout; relaxing that touches sharedcode and is left for a follow-up.
Release
After merge, tag a release so fslabs/fsl_libs#4230 can set its pin
(version + binary sha256) in
.github/actions/install-fslabscli.