main
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| c100d4a2d7 |
ci(bundle): compose and publish the bundle manifest
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
Phase 0 item 3 of docs/installer/PLAN.md (§7.1-§7.3). The installer resolves what to install *from* the bundle, so this has to exist before Phase 1 code is useful. Both components it composes now have releases, which is what unblocked it. Adds .gitea/workflows/bundle.yml — resolve both components' latest releases, run the two compose-time gates, and publish bundles/current.json — plus the first real bundle (2026.08.04: link v1.1.0 + overlay v0.1.1, protocol 3). Bundles are COMMITTED under bundles/, not published as releases. This repo's own releases are the installer binaries, and /releases/latest returns whichever release is newest regardless of kind, so interleaving bundle releases would make "latest" intermittently resolve to a release carrying no installer binary. The push to main needs no new branch-protection exception: release.yml's version-bump commit already requires it. Gate 1 (protocol agreement) reads the sidecar's PROTOCOL_VERSION from sidecar/src/main.rs at the release tag, not from the binary. --print-config would answer, but only for releases from v1.1.0 on, and --bundle <tag> has to be able to recompose an older bundle. It also avoids executing a downloaded artifact and provisioning a throwaway config whose auth token would land in a CI log. The overlay half comes from manifest.json inside the tarball, which is the only statement of that version that exists. Gate 2 (assets) downloads every asset and verifies it against the SHA256SUMS its publishing repo shipped, then records the hash it computed itself. These artifacts are deliberately unsigned, so a hash copied from a file nobody checked would make the whole chain decorative. An asset with no SHA256SUMS entry is caught separately, since `sha256sum -c` passes right over it. Release reads are ANONYMOUS on purpose: they are exactly the requests the shipped installer makes on a host with no Gitea credentials, so a repo flipped to private fails here rather than on an operator's machine. Stale components (§7.3) are dispatched, never awaited — Gitea's dispatch endpoint returns no run handle. "Ahead of its release" counts only releasable commits and excludes merge commits, whose subject quotes the feat/fix title and would otherwise re-dispatch a workflow that correctly declines to run, every night. A run that finds nothing changed writes nothing, comparing everything except `bundle` and `generated` — that is what makes the nightly cron free rather than a dated duplicate every morning. Verified by running the workflow's exact compose steps in a Linux container against the live releases: both gates pass, the composed bundle is the file committed here, a re-run reports changed=false, and the stale-detection filter scores 1 releasable commit for link v1.0.0..main (excluding the merge that quotes it) and 0 for a docs-only range. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 60ae6f1f75 |
fix(ci): preflight release credentials and recover from an orphan tag
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
servuo-plugins hit both of these on its first real release run; this repo runs the same engine, so it has the same two defects latent. REGISTRY_USER / REGISTRY_TOKEN were empty there, yet the tag push SUCCEEDED: actions/checkout leaves an `http.<host>.extraheader` credential in the local git config, so `git remote set-url` to a URL with empty credentials still authenticated through that leftover header. The release API call had no such fallback and returned 401. Net result was the worst available outcome — the repo tagged, no release, and a failed job. Two fixes: A credential preflight, before anything is built or pushed, gated on the run actually intending to publish so a docs:/chore:-only merge (or this repo's pre-crate no-op) still passes on a repo with no secrets. It names the missing secrets and the scope they need instead of failing wherever they happen to be used first. Orphan-tag recovery. A tag with no release behind it means an earlier run died after tagging, and the old code treated any existing tag as "nothing to release" — so that state could never clear itself: every later run would see the tag and stand down, forever. The plan step now asks the API whether a release exists for the tag, and on 404 reuses the tag and publishes the release it is missing. This deliberately overrides the RELEASE=false the bump logic just decided, which is the whole point — with the tag in place there are no releasable commits after it. Anything other than 200/404 (network failure, bad token) is refused rather than guessed, since assuming "no release" would republish over a good one. The tag step now reuses an existing tag instead of failing on `git tag`, and the changelog for a recovery run summarizes what the tag contains (previous-tag..this-tag) rather than the empty range after it. sync-project-tree gets the same preflight: its first run on main failed with an opaque `git clone` error against `https://:@host/...` that said nothing about a missing secret. Verified by extracting every run block and exercising the paths: empty secrets fail the preflight with a legible message and populated ones pass; the no-Cargo.toml guard still short-circuits to release=false; a crate with no tag still takes the seed path; and against real repo state, a tag with a release stands down while an orphan tag recovers. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| ea9aad6e9b |
ci(installer): add pr-checks, release, and project-tree sync workflows
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 11s
Bring this repo's CI up to parity with the other Runic Gateway repos. All three are retargeted from RunicGateway/link, which is the closest analog (same Rust toolchain, same release engine, same runner). pr-checks.yml Gates PRs into main on cargo fmt --check, clippy -D warnings, and cargo test --locked, in that order, one job — mirroring release.yml's gates so a green PR implies a green release. release.yml The conventional-commit release engine from link/, with the Rust adapter retargeted: crate at the repo root, binary runicgateway-installer, cross-compiled for x86_64 Linux and Windows. Artifact names follow PLAN.md §3. The generated changelog now carries the checksum-verification block, because releases are deliberately unsigned and SHA256SUMS is the trust anchor (PLAN.md §3) — that makes the verify instructions part of the release, not a doc someone has to find. sync-project-tree.yml (+ .gitea/scripts/gen_tree.py) Regenerates docs/installer/PROJECT_TREE.md on every push to main and opens or force-updates a PR against the docs repo. Verbatim from link/ apart from the repo/path/label env block. Crate guard This repo has no Cargo project yet — Phase 1 creates it. Landing the workflows unguarded would red-X every governance and docs PR until then, and holding them back leaves the repo ungated exactly while its conventions are being set. So both Rust workflows check for a root Cargo.toml first: pr-checks skips its gates with a notice, and release.yml's plan step sets RELEASE=false and exits. Both arm themselves the moment Cargo.toml lands, with no edit here. Verified before pushing: all three files parse as YAML, every run block passes bash -n, and the release plan step was simulated against a throwaway git repo both without a crate (release=false, exit 0) and with one (first-release path -> v0.1.0 with the changelog rendered). Not included: the bundle-manifest workflow (PLAN.md §7) and the release-dispatch hook, which are Phase 0 item 3 and depend on servuo-plugins having a release workflow first. Note for setup: release.yml and sync-project-tree.yml need REGISTRY_USER and REGISTRY_TOKEN (write:repository, plus read/write on RunicGateway/docs) configured for this repo. Co-Authored-By: Claude <noreply@anthropic.com> |