feat(scripts/spectro): automate the Spectro Cloud Palette pack - #271
Open
ausbru87 wants to merge 3 commits into
Open
feat(scripts/spectro): automate the Spectro Cloud Palette pack#271ausbru87 wants to merge 3 commits into
ausbru87 wants to merge 3 commits into
Conversation
Adds a generator and a release-triggered workflow that keep the Coder community pack in spectrocloud/pack-central in step with the stable Coder release. The existing pack was published as coder-2.23.3 but ships upstream chart content from 2.21.3, and nests its chart values one level too shallow so every Palette override is silently discarded. Both defects came from hand edits, so the pack is now generated from the published chart and asserted rather than assembled by hand. The workflow subscribes to the coder-release repository_dispatch that coder/coder already sends here, gated on release_channel == stable, and opens a pull request against pack-central from a bot fork. A weekly drift check opens a tracking issue if the pack falls behind the stable release.
shellcheck SC2155: masking the return value of the subshell.
helm package stamps the gzip header and file mtimes, so rebuilding the same Coder version produced a different tarball each run and would have shown up as a spurious diff on every pipeline re-run. Repack with sorted entries and zeroed mtime, owner and gzip name field, then assert helm can still read the result.
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.
Summary
Adds a generator and a release-triggered workflow that keep the Coder community pack in spectrocloud/pack-central in step with the stable Coder release.
Companion pull request adding the regenerated pack: spectrocloud/pack-central (linked once opened).
Problem
The Coder pack in pack-central is
coder-2.23.3, contributed by hand in July 2025. Stable is now 2.35.6. It is not only stale, it is wrong in two ways, and both came from hand edits:Chart.yamlsay 2.23.3, but every template and value byte-matches upstream 2.21.3.Chart.yamlwas version-bumped without re-vendoring the chart body, and the archivecharts/coder-2.23.3.tgzinternally reportsversion: 2.21.3.charts.coder.env, but the chart's root key iscoder:, so the correct path ischarts.coder.coder.env. Rendering the chart with the pack's own values leaves the defaults in place. Every knob, includingimage,ingress,service,tlsandresources, is silently discarded today.The second defect also means the pack fails pack-central's current CI.
validator/check-values-structure.pylanded in pack-central PR #308 a month after the Coder pack merged, and the existing pack produces 27 errors against it. Any future PR touchingpacks/coder-*trips this.Palette community packs are maintained by the contributing vendor, not by Spectro Cloud, and Palette deprecates a pack minor once two newer minors exist, then disables it after three months and deletes it three months later. Without automation this pack ages out of Palette on its own.
Fix
scripts/spectro/build-pack.sh <version>buildspacks/coder-<version>/from the published Helm chart. Nothing is hand-edited. It refuses to emit a pack unless:https://helm.coder.com/v2/index.yaml;versionandappVersionequal the requested version, and the repackaged archive agrees with its own filename (the check the 2.23.3 pack would have failed);pack.jsonstill carriesname: coder-chartanddisplayName: Coder, since changing either forks a new pack in Palette;charts.codermirrors the chart's root keys and pack-central's owncheck-values-structure.pyexits 0;charts.codersubtree producesghcr.io/coder/coder:v<version>, and an override probe injected into that subtree reaches the rendered manifests. A pack with inert values renders identically either way, which is precisely why the current breakage went unnoticed;crane manifestresolves every entry inpack.content.images;.github/workflows/publish-spectro-pack.yamlsubscribes to thecoder-releaserepository_dispatchthatcoder/coderalready sends to this repository, so no change tocoder/coderis required. It is gated onrelease_channel == 'stable', matching the conditionalaws/submit_ce.shalready uses. It syncs a bot fork, guards against an already-open PR, and opensUpgrade coder pack to <version>against pack-central. A second job runs a weekly drift check and files a tracking issue if the pack falls behind, because silent rot is the failure mode that produced this situation.The three Spectro-specific
Chart.yamledits are applied line-wise rather than withyq, becauseyqreflows sequence indentation and would make every future pack diff noisy against the previous version directory.Verification
Generated 2.35.6 and ran pack-central's real validator against the output:
The same validator against the in-tree
coder-2.23.3reportsStructural + duplicate check failed for 'charts.coder'with 27 path errors.pack.jsondiff against 2.23.3 is two lines, the version and the chart path.logo.pngis byte-identical.Chart.yamldiffers from the previous pack by exactlyversionandappVersion.Follow-ups and open questions
CDRCI_GITHUB_TOKENin this repository. Referenced bycoder/coderfor the same bot identity, but repo-level secrets here are not readable with my access. Needs confirming before the first real run.cdrciholds a fork ofspectrocloud/pack-central. The owner is read fromvars.SPECTRO_FORK_OWNER, defaulting tocdrci; the first run fails loudly atgh repo syncif the fork is missing.vars.SPECTRO_PACK_REVIEWERSis unset, so the cc step no-ops. Set it to route pack PRs to a human.CODEOWNERSis new and deliberately scoped to the Spectro paths only. Adjust or drop the handles if that is not the right routing.coder/coderpins by SHA. Happy to switch if the org wants SHA pinning on new files here.scripts/spectro/README.mdand is designed as a separate secret-gated job so this workflow keeps working if the tenant is delayed.Implementation plan
Keeping the Coder pack in
spectrocloud/pack-centralup to dateDirect answer
Yes — build the pipeline in
coder/packages. The trigger already exists and already points there:coder/coder's release workflow firesrepository_dispatch: coder-releaseatcoder/packageswith{coder_version, release_channel}. Three workflows there already consume it. A Spectro job is a fourth subscriber to a working event bus, requiring zero change tocoder/coder.But a pipeline alone does not fix this. The pack is not merely outdated — it is mislabeled and functionally broken, and it fails pack-central's current CI. A remediation PR must land first, by hand, before any automation is worth wiring up.
What the research found
The pack is worse than "outdated"
coder-2.23.3; content is upstream 2.21.3diff -ruofpacks/coder-2.23.3/charts/coderagainstcoder_helm_2.21.3.tgzis empty except a 3-lineChart.yamlpatch. Against 2.23.3 it differs by exactly the upstream 2.21.3→2.23.3 delta. The shippedcharts/coder-2.23.3.tgzreportsversion: 2.21.3, appVersion: 2.21.3. Someone hand-editedChart.yamlwithout re-vendoring.values.yamlnests chart keys atcharts.coder.env, but the chart's root key iscoder:, so the correct path ischarts.coder.coder.env. Proven:helm templatewithcharts.coder.image.repooverridden still rendersghcr.io/coder/coder. Correct nesting works. Every knob —env,image,ingress,service,tls,resources— is silently discarded in Palette today.python3 validator/check-values-structure.py→ 27 errors, exit 1. Five recently-merged packs → exit 0. Coder is the outlier. This check landed in PR #308 (2026-08-25), a month after the Coder pack merged (#136, 2025-07-27). Any PR touchingpacks/coder-*triggers it.The nesting bug was flagged during the original review —
vishwanathson PR #134: "codershould be within parentcharts:... This is the format to be followed" — and merged anyway.How pack-central contribution actually works
Fork-and-pull. Add a new
packs/<name>-x.y.z/directory; nothing is ever edited in place, nothing is ever deleted. Required per version:pack.json,values.yaml,logo.png,README.md,charts/<chart>-<ver>.tgz(+ unpacked tree, the 174/237 convention).CI (
pack-validation.yml, no secrets, fork-safe) enforces:pack.jsonJSON-schema validity;versionwith no leadingv;logo.pngandREADME.mdpresent; everycharts[]/kubeManifests[]path exists;charts.<chartName>in pack values is a structural subset of the chart's ownvalues.yaml;pack.content.imagesnon-empty and each image resolvable viacrane manifest. Bulwark gitleaks + CVE scans also run; CVE findings are informational (PR #303 merged with 235).Identity rule:
pack.jsonname(coder-chart) anddisplayName(Coder) must stay byte-stable across versions or Palette treats it as a brand-new pack.Human gates that no bot can shorten:
vishwanathsmerged 58 of the last 60 PRs. Median time-to-merge 4.2 days (p90 15d). 40 PRs open now, oldest 2.5 months.push-packs.ymlisworkflow_dispatch-only with Spectro-held registry secrets. Stated cadence: biweekly, Tuesdays and Thursdays. Merge ≠ published.Deprecation clock: Palette deprecates a pack minor once two newer minors exist → 3 months Deprecated → 3 months Disabled → Deleted. A vendor that stops publishing ages out of Palette automatically. Community packs are explicitly "not actively maintained by Spectro Cloud; updates depend on community contributors."
Re-vendoring is trivial
Upstream publishes a fully self-contained chart four redundant ways, all byte-identical (verified sha256 match between the HTTP tarball and the OCI artifact):
https://helm.coder.com/v2/coder_helm_X.Y.Z.tgzhelm pull coder/coder --version X.Y.Zhelm pull oci://ghcr.io/coder/chart/coder --version X.Y.Zcoder_helm_X.Y.Z.tgzlibcoderis pre-vendored inside — nohelm dependency update, no submodules.index.yamlcarries a per-version digest for integrity checking.The only genuine Spectro delta is a 3-line
Chart.yamlpatch (repository: file://libcoder,icon: file://assets/icons/coder.png,kubeVersion: '>=1.19-0') plus dropping the upstreamREADME.md.2.21.3 → 2.35.6 is additive only
Nothing removed, nothing renamed, no new required values. Two new opt-in templates (
httproute.yaml,listenerset.yaml, both default-off, Gateway API). New defaulted keys:priorityClassName,podSecurityContext,serviceAccount.workspaceNamespaces,serviceAccount.labels,hostAliases,readinessProbe.*,livenessProbe.*,listenerset.*,httproute.*.libcoder/_rbac.yamlwas refactored for multi-namespace RBAC — behaviorally equivalent in the default single-namespace case.One behavior change to document:
coder.livenessProbe.enablednow defaults tofalse(was unconditionally on in 2.21/2.23).Exactly one container image, so
pack.content.imagesstays a single entry:ghcr.io/coder/coder:vX.Y.Z(note thevon the image tag; novon the pack/chart version).2.35.6 (stable) and 2.36.3 (mainline) are template-identical — only
Chart.yamldiffers. Targeting stable costs nothing.Precedent for the automation shape
coder/coder's release workflow already opens PRs against third-party repos:publish-winget→microsoft/winget-pkgs— the true analogue.gh repo sync cdrci/winget-pkgs, submit from a bot-owned fork withCDRCI_GITHUB_TOKEN, then locate the PR andgh pr commenta cc for human tracking.publish-homebrew→coder/homebrew-coder— rawgit+gh, branchauto-release/$version, with an idempotency guard:gh pr list --search "head:$branch", bail if one is already open.coder/packagesis definitionally the marketplace repo (AWS Marketplace AMI + Container/EKS, GCP Marketplace, ECR, Snap Store) and already carriesaws/helm_values.yaml. It already implements stable-gating in the exact shape needed (submit_ce.sh).greg-the-coder— who contributed the Spectro pack — is an activecoder/packagescommitter.Decisions taken
client_payload.release_channel == 'stable'. ~1 PR/month, comfortably outruns the deprecation clock, and does not pile onto a 40-PR backlog.Plan
Phase 0 — Remediation PR to pack-central (blocking; do this first)
Nothing downstream matters until the pack is correct. Hand-drive one PR that adds
packs/coder-2.35.6/, built from upstream 2.35.6 with the nesting bug fixed.packs/coder-2.35.6/; copylogo.pngverbatim fromcoder-2.23.3.charts/, apply the 3-lineChart.yamlpatch, delete upstreamREADME.md, syncChart.lock'srepositoryfield for consistency. Do not hand-editversion/appVersion— they already read 2.35.6. Repackage withhelm packageand assert the produced tgz reports 2.35.6.pack.json: change onlyversionandcharts[0].name: "coder-chart"anddisplayName: "Coder"stay byte-identical.values.yaml, do not hand-merge. Emit thepack:header, then indent the chart's ownvalues.yamlfour spaces undercharts.coder:. This producescharts.coder.coder.*,charts.coder.provisionerDaemon.*,charts.coder.extraTemplates— the layout the validator and Palette expect — and fixes the inert-values bug for free.README.mdagainsttemplates/README-template.md(Title / Prerequisites / Parameters table / Upgrade / Usage / References). The current README uses## Resourcesand has no Parameters table. The Upgrade section must call out thelivenessProbe.enabled: falsedefault change and that overrides now actually take effect.Upgrade coder pack to 2.35.6, matching repo convention. Body names the validated scenarios and states plainly that 2.23.3 shipped 2.21.3 content with non-functional overrides. PR carries the Coder Agents disclosure.Open decision inside Phase 0: the published 2.23.3 pack is live in the prod registry deploying 2.21.3 with all config ignored. Options: (a) supersede silently, (b) an in-place
+2 -2correction to 2.23.3 (precedent: PRs #294, #251), or (c) request removal of the broken version (precedent: PR #304, vendor-initiated). Recommend (a) plus a note in the PR body, escalating to (c) only if Spectro asks.Phase 1 — Generator script in
coder/packagesLand
scripts/spectro/build-pack.sh <version>behind its own PR, tested viaworkflow_dispatchbefore any dispatch trigger is wired. Pure function: version in,packs/coder-<version>/out. Steps 1-7 above, scripted. Vendorvalidator/check-values-structure.py's contract as the local oracle, or fetch it from pack-central at runtime and pin by SHA.Guardrails the script must assert, because they are the easy ways for a bot to break Palette:
name == "coder-chart"anddisplayName == "Coder"unchanged.versionhas no leadingv; the image tag does.pack.json.version,pack.json.charts[0]filename,Chart.yamlversion/appVersion,pack.content.images[0],pack.content.charts[0].version.charts.codernode exists and is a mapping; structural-subset check exits 0.values.yamluses spaces only, 2-space multiples, no trailing whitespace. (validate-values.shis currently dead in CI due to a stale^stable/matcher, but conform pre-emptively.)Phase 2 — Workflow
coder/packages/.github/workflows/publish-spectro-pack.yamlGated
if: github.event.client_payload.release_channel == 'stable' || github.event_name == 'workflow_dispatch'— the same conditionalaws/submit_ce.shalready uses.Steps: run the Phase 1 generator →
gh repo sync <fork>/pack-central -b main(winget pattern) → idempotency guardgh pr list --repo spectrocloud/pack-central --head "<fork-owner>:$branch", bail if open (homebrew pattern; essential here, since Spectro's 4-day median merge means PRs will routinely still be open when the next Coder release lands) → push branchauto-release/coder-pack-$VERSION→gh pr create --repo spectrocloud/pack-central.PR body: version, upstream chart digest, the offline checks that passed, and an explicit note that it is automated from
coder/packages.On success,
gh pr commenta cc to the marketplace owners, mirroring winget — the dispatch is fire-and-forget, so a failure here is otherwise invisible from thecoder/coderrelease run.Phase 3 — Palette validation (deferred until the partner tenant exists)
Add as a separate job gated on secret presence, so Phase 2 keeps working unchanged if the tenant is delayed or revoked. Design the seam now:
oras pushthe pack to a GHCR repo under<org>/spectro-packs/archive/coder-chart:<version>. Palette requires that exact namespace path. ORAS must be pinned to v1.0.0 — Spectro documents this as a hard requirement.POST /v1/registries/oci/basic(once, or reuse a persistent registry UID) →POST /v1/registries/oci/{uid}/basic/sync→ poll/sync/status.POST /v1/clusterprofiles/validate/packs— schema-validates a profile using the pack without deploying a cluster, so no cloud spend and no 15-30 minute wait.GET /v1/packs/{packName}/registries/{registryUid},/readme,/logoto confirm it rendered.Auth is an
apiKey: <key>header, tenant-scoped. Rate limits are 10 rps on/v1/packsand/v1/registries. Registry must be reachable inbound from Palette SaaS — GHCR satisfies this; a self-hosted throwaway registry would not without tunneling.When this lands, extend the PR body to name the validated scenarios, which is what pack-central reviewers explicitly ask for.
Phase 4 — Housekeeping in
coder/packagesCODEOWNERSnaming the marketplace maintainers.coder/packageshas none, so there is no review routing today.Prerequisites to confirm with an admin
These could not be resolved with the available token (
gh api repos/coder/packages/actions/secrets→ 403):CDRCI_GITHUB_TOKENalready available tocoder/packages? It is wired intocoder/coder, butcoder/packagescurrently uses cloud credentials, not GitHub PATs. This is the one genuinely new capability the plan needs.cdrcimachine account hold aspectrocloud/pack-centralfork? pack-central mandates fork-and-pull.coder/openshift-deploymentorcoder/aws-deployment(both INTERNAL, contents inaccessible) contain competing marketplace automation?Risks
gh release view --repo coder/coder --json tagName(which authoritatively returns stable, not newest) against the highestpacks/coder-*directory, and open an issue on mismatch.check-values-structure.pychanges upstream and breaks generationname/displayNameare never templated.CHANGES_REQUESTEDfluentbithas 12 live directories. No action.Success criteria
packs/coder-2.35.6/merged, withcheck-values-structure.pyexit 0 andhelm templaterenderingghcr.io/coder/coder:v2.35.6.coder-releasedispatch produces a correct pack PR with no human in the loop up to the point of Spectro's review.Raised by Coder Agents on behalf of @ausbru87.