Commit Graph

5 Commits

Author SHA1 Message Date
ab2efb62e4 feat(rust): RunicNPC as the Rust bundle's third artefact (runicnpc stage 4, D224)
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m27s
The compose job carries the latest RunicNPC release that answers the API the
bridge's manifest declares (runicnpc_api), verified against its SHA256SUMS
like the other two; none when the bridge needs none, RunicNPC has not
released, or its API is too old, each said in the summary. Walked against a
mock Gitea in all three cases.

The installer reads the optional npc component (older bundles still parse),
fetches and checks RunicNPC's tarball against its manifest, places
RunicNPC.cs before the bridge in each instance's plugins directory, records
it, puts it back on update when edited or deleted, reports it in doctor, and
removes it on uninstall. Its data directory is never touched. Kits joins the
required plugins it reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-30 04:52:36 -05:00
4f268f7ffb ci(bundle): compose schema 2 for ServUO and Rust beside schema 1
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m12s
Module-rust phase 18, step 2 of docs/modules/rust/PLAN.md §34.2.7 (D146,
D147). The compose job now writes three streams on the bundles branch:

  current.json         schema 1, ServUO, exactly as before
  v2/servuo/           schema 2, game "servuo"
  v2/rust/             schema 2, game "rust" (Rust-Link + Rust-Plugins)

The gates move into .gitea/scripts/compose-bundles.sh, so they can be run by
hand against the real release API or a mock without a runner. Each game
composes independently: a Rust failure still lets a ServUO bundle publish, and
the run goes red afterwards. A game whose repos have never released composes
nothing and does not fail, which is where Rust is until its cutover.

A ServUO matrix carries the same tag at both schemas, so the first run
publishes v2/servuo/bundle-2026.09.15.json rather than a second name for the
pair. Schema 1 stops being composed on 2027-01-01 and is left frozen.

Tested in debian:12 against the real releases (v2/servuo published as
2026.09.15, schema 1 unchanged, Rust skipped; rerun is a no-op; past the
retirement date schema 1 is frozen) and against a mock Gitea for Rust
(document composed, protocol mismatch refused, missing manifest key refused,
rerun a no-op, ServUO failing independently).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-25 22:49:47 -05:00
7db58031c7 fix(bundle): publish to a bundles branch, and unbreak the stale check
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 6s
Three things, all found by the first compose run that ever had a bundle
to write.

1. `main` is protected, so the push was declined by the pre-receive
   hook -- twice, since the retry rebases and pushes to the same place.
   Every bundle since v1.1.1 has been composed correctly and thrown
   away. Bundles now go to a `bundles` branch of their own, at its
   root, which needs no protection exception and keeps everything the
   original choice was for: a reviewable diff, a git history of the
   compat matrix, plain anonymous raw URLs, no credentials on the shard
   host. The header's claim that this push "needs no new
   branch-protection exception" was simply false.

2. A `${{ }}` written literally in a shell comment silently disabled
   the entire stale-component check. The runner scans a step's script
   for template expressions before running it, fails to parse the empty
   one, and skips the step WITHOUT failing the job -- so the dispatch
   that is supposed to fire a component's release workflow has never
   run once. Reworded, with a warning not to write that token in a
   comment again. (link/release.yml and this repo's release.yml carry
   the same bug in their bump-and-tag step; handled separately.)

3. linux-aarch64 is now a REQUIRED platform key, which was step 3 of
   PLAN.md §5.2 and was waiting on link publishing one. v1.1.1 does, so
   from here a dropped target reddens this job instead of vanishing
   from every bundle.

The published bundles are materialized into a worktree at `published/`,
so the ".2 suffix" scan and the idempotence check read what is actually
published rather than a stale copy on main. The branch is created from
an empty-tree root commit on first use, so it carries no history that
has nothing to do with the compat matrix; it has been seeded already
with bundle 2026.08.04, because every bundle is kept forever and the
move must not lose the one that exists.

bundles/*.json is deleted from main -- it is now a stale copy of data
that lives elsewhere, and a wrong "current" is worse than none. The
README stays and documents the branch.

Verified by running the whole job in a container against a bare repo
standing in for the remote: first run creates the branch and publishes
both files with all three asset keys, second and third runs report
"identical to the published current.json -- nothing to publish" and
push nothing, and the stale check now runs and reports both components
as having nothing releasable.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 17:14:57 -05:00
fd59a74912 ci(bundle): recognize a linux-aarch64 link asset
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 5s
Step 1 of PLAN.md §5.2's four, and it has to be first. Two rules in this
job are strict in opposite directions: an unrecognized link asset name
fails the run, and a missing REQUIRED platform key fails it too. So the
name must be taught before the release that carries it, and the key can
only be required after one exists -- requiring it first would fail every
bundle for as long as the gap lasts.

This is therefore the mapping only. linux-aarch64 is not in REQUIRED
yet; step 3 promotes it once a link release actually ships the binary,
after which a dropped target reddens CI instead of vanishing silently
from every bundle.

The compose step needed no change: it builds the asset map from the
platform TSV, so a third key costs it nothing.

Edited on `main` and deliberately not on `edge`. The compose job runs
from `main`, and leaving `edge`'s copy untouched means the eventual
cutover merge has nothing to conflict over.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:22:59 -05:00
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>
2026-08-04 11:10:51 -05:00