Skip to content

feat(orchestrator): build-lifecycle capabilities (symbols, visual baseline, provisioning, service directory, anti-cheat) - #144

Merged
frostebite merged 2 commits into
mainfrom
feat/orchestrator-lifecycle-capabilities
Aug 24, 2026
Merged

feat(orchestrator): build-lifecycle capabilities (symbols, visual baseline, provisioning, service directory, anti-cheat)#144
frostebite merged 2 commits into
mainfrom
feat/orchestrator-lifecycle-capabilities

Conversation

@frostebite

Copy link
Copy Markdown
Member

Implements five capabilities as real orchestrator features, replacing the standalone plugin skeletons drafted in #128, #130, #132, #133 and #136 (now removed from #142).

Why these belong here

A plugin exists to add user-facing command surface — a new verb, engine, or deploy target (steam-deploy, runtime-test-framework, the engine plugins). None of these five are that. They are things that happen to a build or a running job, which is exactly orchestrator's domain: it already owns providers/ (where a job runs) plus services/ for cache, hooks, output, preflight, reliability, secrets and sync.

Two overlapped existing orchestrator surface outright:

  • anti-cheat registered options: and no command at all — it hooks into an existing build, which is precisely services/hooks/middleware-service.
  • screen-capture duplicated the built-in images output type ("Screenshots, render captures, atlas previews").

What landed

symbol-collector.ts + symbols output type — finds dSYM bundles, PDB, Breakpad, DWARF and IL2CPP maps. Sits beside the existing coverage/logs/metrics built-ins, because symbols must be captured at build time or they're gone: once the machine is torn down, every future crash report from that build is unsymbolicatable. A .dSYM is reported as one bundle entry rather than descended into — the symbolicator needs the bundle intact. Uploading is left to the existing ArtifactUploadHandler; no vendor-specific upload path here.

visual-baseline.ts + visual-baseline output type — digest-based comparison against the accepted reference set. Deliberately not perceptual diffing: a byte hash answers "did this change at all" exactly with no image codec, and pretending it were a threshold diff would be worse than not offering one. An empty baseline reports as unverified rather than a pass, so the check can't go vacuous on a first run or after an accidental baseline deletion.

dedicated-server-provisioner.ts — docker-compose, systemd unit and ufw rules from a typed config. Pure functions: nothing written, nothing executed, so a generator bug can't mutate a real host. Port protocol is always explicit (a UDP game port published as TCP gives a server that starts cleanly and is silently unreachable); the unit is ordered After=docker.service so a reboot doesn't race the daemon; it doesn't run as root by default.

service-directory.ts — registry for exposed job endpoints, where the disclosure rules are the actual content. An ephemeral tunnel URL is an unauthenticated entry point into a machine holding source and credentials, and CI logs are often public or archived — so visibility is explicit per service, and formatForLog redacts the whole URL. A random subdomain is the secret, so partial masking would still leak the reachable address. It doesn't start tunnels; a caller registers whatever URL it resolved (cloudflared, ngrok, LAN, k8s Service).

anti-cheat-middleware.ts — preset over the existing Middleware type. Runs at post-build (ahead of default-priority packaging, or an unprotected binary ships) with allowFailure forced to false and not configurable — a build that skipped its integrity step but still produced a shippable player is the worst outcome, since it looks successful. The SDKs are NDA-gated, so the command is caller-supplied rather than guessed, and credentials go through OrchestratorSecret instead of being interpolated into a shell string.

All five are exported from the package entry point.

Verification

  • 50 new tests, passing under both vitest and bun. They avoid vi.mock(module, fn) — which is what makes the 244 pre-existing orchestrator tests fail under bun's runner — so this adds none to that baseline.
  • tsc --noEmit clean; oxfmt applied to the new files only.
  • Build artifacts (plugins/unity/dist, bun.lock) deliberately reverted rather than committed.

Merge order

Please merge this before #142#142 has already had these five skeletons removed, so landing this first keeps the catalog coherent at every commit.

…eline, provisioning, service directory, anti-cheat)

Implements five capabilities that were drafted as standalone @game-ci/*
plugin skeletons (#128, #130, #132, #133, #136) as real orchestrator
features instead.

A plugin exists to add user-facing command surface - a new verb, engine,
or deploy target (steam-deploy, runtime-test-framework, the engine
plugins). None of these five are that. They are things that happen *to* a
build or a running job, which is what orchestrator already does: it owns
providers/ (where a job runs) plus services/ for cache, hooks, output,
preflight, reliability, secrets and sync. Two of them overlapped existing
orchestrator surface outright - anti-cheat registered options and no
command at all (that is middleware-service), and screen-capture duplicated
the built-in `images` output type.

What landed:

  symbol-collector.ts + `symbols` output type - finds dSYM bundles, PDB,
  Breakpad, DWARF and IL2CPP maps under a build. Sits beside the existing
  coverage/logs/metrics built-ins because symbols must be captured at
  build time or they are gone: once the machine is torn down, every future
  crash report from that build is unsymbolicatable. A .dSYM is reported as
  one bundle entry rather than descended into, since the symbolicator
  needs the bundle structure intact. Uploading is left to the existing
  ArtifactUploadHandler - no vendor-specific upload path here.

  visual-baseline.ts + `visual-baseline` output type - digest-based
  comparison of this run's captures against the accepted reference set.
  Deliberately NOT perceptual diffing: a byte hash answers "did this
  change at all" exactly, with no image codec, and pretending it were a
  threshold diff would be worse than not offering one. An empty baseline
  reports as unverified rather than as a pass, so the check cannot go
  vacuous after an accidental baseline deletion.

  dedicated-server-provisioner.ts - docker-compose, systemd unit and ufw
  rules from a typed config. Pure functions; nothing is written or
  executed, so a generator bug cannot mutate a real host. Port protocol is
  always explicit (a UDP game port published as TCP yields a server that
  starts cleanly and is unreachable), the unit is ordered after
  docker.service so a reboot does not race the daemon, and it does not run
  as root by default.

  service-directory.ts - registry for exposed job endpoints, with the
  disclosure rules as the actual content. An ephemeral tunnel URL is an
  unauthenticated entry point into a machine holding source and
  credentials, and CI logs are often public, so visibility is explicit per
  service and formatForLog redacts the whole private URL - a random
  subdomain IS the secret, so partial masking would still leak it. Does
  not start tunnels; a caller registers whatever URL it resolved.

  anti-cheat-middleware.ts - preset over the existing Middleware type,
  running at post-build (before default-priority packaging, or an
  unprotected binary ships) with allowFailure forced false. The SDKs are
  NDA-gated so the command is caller-supplied rather than guessed;
  credentials go through OrchestratorSecret instead of being interpolated
  into a shell string.

All five are exported from the package entry point.

Tests: 50 new, passing under BOTH vitest and bun - they avoid
vi.mock(module, fn), which is what makes the 244 pre-existing
orchestrator failures fail under bun's runner, so this adds none of them.
tsc --noEmit is clean and oxfmt has been applied.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 46 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 74f7c1b5-916c-4ed8-9a83-ac224187fd8b

📥 Commits

Reviewing files that changed from the base of the PR and between 895258f and ad832f9.

📒 Files selected for processing (13)
  • plugins/orchestrator/src/index.ts
  • plugins/orchestrator/src/model/orchestrator/services/hooks/presets/anti-cheat-middleware.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/hooks/presets/anti-cheat-middleware.ts
  • plugins/orchestrator/src/model/orchestrator/services/network/service-directory.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/network/service-directory.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/artifact-service.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/output-type-registry.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/symbol-collector.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/symbol-collector.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/visual-baseline.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/output/visual-baseline.ts
  • plugins/orchestrator/src/model/orchestrator/services/provisioning/dedicated-server-provisioner.test.ts
  • plugins/orchestrator/src/model/orchestrator/services/provisioning/dedicated-server-provisioner.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The registry tests assert the number of built-in types, so adding
`symbols` and `visual-baseline` broke them: 8 -> 10 built-in, and
9 -> 11 for the built-in-plus-one-custom case.

Also adds both new names to the it.each parity list, so they get the
same defined/name/builtIn coverage every other built-in type has rather
than only being counted.

Caught by CI, not locally: I had run only the new test files. Full
`vitest run` now passes except two failures that reproduce identically
on a clean main (an rclone step test, and cli-integration timing out
under parallel load) - both pre-existing and unrelated.
@frostebite
frostebite merged commit 725ccf7 into main Aug 24, 2026
36 checks passed
@frostebite
frostebite deleted the feat/orchestrator-lifecycle-capabilities branch August 24, 2026 23:20
frostebite added a commit to game-ci/documentation that referenced this pull request Aug 24, 2026
…ns page

Consolidates #586 (docs: plugin catalog for the 14 new game-ci/cli
plugins) into this PR instead of merging it separately - #586 targeted
docs/03-github-cli/04-configuration-and-plugins.mdx, which this PR
renumbers to 05-configuration-and-plugins.mdx (it inserts
04-orchestrate-advanced/), so the two would otherwise collide on the same
page under different filenames.

Content is updated to match what actually shipped, not #586's original
snapshot:

  - live-show, dev-tunnel, crash-symbol-upload, screen-capture,
    dedicated-server-provisioning and anti-cheat are removed from the
    plugin list - the first was dropped entirely (game-ci/cli#146:
    duplicated runtime-test-framework's player-launching, and the rest
    of its scope - broadcast, an AI-driven playthrough agent - doesn't
    belong in a CI tool), the other five were re-implemented as real
    Orchestrator capabilities rather than plugin skeletons
    (game-ci/cli#144), and are documented in a new "Not plugins:
    build-lifecycle capabilities" section instead.

  - steam-deploy and runtime-test-framework are marked "Implemented,
    loaded by default" rather than folded in with the drafts - they are
    real, working commands, just still subject to change.

  - Added a warning block reflecting game-ci/cli#145: every plugin here
    is experimental, none are published to npm, and each one warns at
    runtime (drafts on load, the two implemented ones when their command
    is actually used).

#586 will be closed as superseded once this merges.
frostebite added a commit that referenced this pull request Aug 24, 2026
…only symbols (#147)

Only crash-symbol collection belongs in the Orchestrator. Debug symbols
have to be captured at build time or they are gone for good, which
genuinely is orchestrator's output-collection domain - it stays as the
`symbols` output type and symbol-collector.ts, from #144.

The other four capabilities from that same PR are moved back to being
plugins, matching the other 10 experimental drafts (#145):

  visual-baseline.ts   -> plugins/screen-capture      (`capture` command)
  service-directory.ts -> plugins/dev-tunnel           (`tunnel` command)
  dedicated-server-provisioner.ts
                        -> plugins/dedicated-server-provisioning
                           (`provision-server` command)
  anti-cheat-middleware.ts is dropped; plugins/anti-cheat is restored to
  its original options-only shape (its actual command, if there ever is
  one, has to come from EasyAntiCheat/BattlEye SDK integration, which is
  NDA-gated and was never real to begin with - the Middleware-based
  wiring only made sense while this lived inside orchestrator).

The ported logic itself is unchanged (same generators, same tests) - only
its home moves. Each of the three command-based plugins gets the same
onLoad warning and `throws "not implemented yet"` command dispatch as the
other drafts; anti-cheat keeps its existing onLoad warning, reworded to
match the standard "[game-ci] WARNING: ... EXPERIMENTAL ..." phrasing
used everywhere else. All four are marked `"private": true` like the
rest, and their READMEs get the standard EXPERIMENTAL banner plus a
"What's real" section pointing at the tested logic underneath.

output-type-registry.ts and its test drop back to 9 built-in types (was
10 with visual-baseline); orchestrator's index.ts drops the four exports.

Fixed while moving: screen-capture's tsconfig resolves a stricter
BinaryLike type for crypto.Hash#update than orchestrator's does, even
with identical @types/node - digestDirectory now passes a Uint8Array view
instead of the raw Buffer, which satisfies both.

Verified: `bun install --frozen-lockfile` passes; each new plugin's suite
passes under both vitest and bun (34 tests, unchanged from before the
move); onLoad/createCommand behavior driven directly (4 onLoad warnings,
3 commands throw on use); orchestrator's output-service suite (69 tests)
still passes with the count fixed to 9 built-ins.
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