Skip to content

ci: rework build/release pipelines + add Thailand GitOps deploy#35

Merged
sangwa merged 5 commits into
mainfrom
ci/build-release-pipelines
Jun 19, 2026
Merged

ci: rework build/release pipelines + add Thailand GitOps deploy#35
sangwa merged 5 commits into
mainfrom
ci/build-release-pipelines

Conversation

@sangwa

@sangwa sangwa commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Reworks the CI/CD pipelines so versioned releases are produced deterministically and the Thai image auto-deploys to siam-ai. Three commits, building on each other.

What changes

1. Split build / release (a60afa6)

  • build.yml (continuous): main pushes + manual dispatch, path-scoped to files that go into the image. No tag trigger. Tags sha-<short> always, latest on main, <branch-slug> on dispatch-from-branch.
  • release.yml (NEW): triggers on vX.Y.Z tags; promotes the already-built sha-<short> image to the X.Y.Z tag via oras tag (byte-identical, drift-free), rebuilding only as a fallback. Mirrors the same mode to the Thai overlay.
  • build-thailand.yml: no tag trigger; path-scoped + workflow_run off the base build, with a dorny/paths-filter skip-gate to avoid double-builds.
  • .dockerignore (NEW), shared by both Dockerfiles.
  • Why split: a paths:-filtered tag trigger is silently skipped (tag pushes introduce zero changed files), so tag handling lives in its own workflow.
  • All producing jobs emit a short-lived deploy-info artifact (image/tag/digest/env) as a deploy fast-path; the registry stays the source of truth.

2. Pin + upgrade actions (a60afa6 + 58da1d7)

  • Every external action pinned to an immutable commit SHA with a version comment, all at their latest major. checkout v7's fork-PR guard verified harmless for our workflow_run usage; package-manager-cache: false added to the test job (no lockfile in repo).

3. Thailand GitOps deploy (73c831f)

  • deploy-thailand.yml (NEW): pins agent-sandbox-thailand in EternisAI/gitops-siam-ai (apps/clusters/oa1/axion/app/release/values.yaml, keys axion.sandbox.image.tag/.digest) → Flux reconciles onto oa1-siam.
    • workflow_run after "Release (promote tag)" succeeds → deploys the released semver (tag read from that run's deploy-info-siam-ai artifact).
    • workflow_dispatchinputs.image_tag, else the branch's moving tag (latest/<slug>).
    • Digest always re-resolved from GHCR via oras (source of truth); fails if the tag isn't published.
    • values.yaml edited with a surgical awk line-replace anchored on the sandbox image's repository: line — not yq (the file is human-maintained with comments/alignment yq would reflow). Idempotent; pushed via a rebase-retry loop as the Eternis DevOps Bot.
    • Runs in the Axion Siam.AI GitHub Environment (holds GITOPS_PAT). No reconcile-watch (out of scope).

Verified

  • awk edit against the real values.yaml: touches exactly the two sandbox lines, alignment/quoting preserved, idempotent re-apply = no-op, result valid YAML.
  • actionlint (incl. shellcheck on run: blocks) clean; all workflow YAML parses.
  • Axion Siam.AI environment + GITOPS_PAT secret exist in this repo; no branch policy / reviewers to block workflow_run or branch dispatch.

Notes for reviewers / rollout

  • The GITOPS_PAT scope (fine-grained, Contents: write on gitops-siam-ai) can only be confirmed by a run; safe smoke test once merged: dispatch deploy-thailand on main with empty image_tag → resolves latest, no-op if already current.
  • Deploy triggers go live only once these files are on main.
  • Staging/production deploys remain forthcoming; CLAUDE.md documents the design.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an automated GitOps deployment workflow for the Thailand overlay image using digest-pinned updates to values.yaml.
    • Added a release workflow that promotes CI-built images to semantic version tags and produces deployment metadata artifacts.
  • Chores
    • Reduced Docker build context size with a new .dockerignore.
    • Reworked CI build/release flows with more precise triggers, improved tagging, concurrency controls, and deploy-info artifact generation.
    • Pinned GitHub Actions to commit SHAs for better reliability and security.
  • Documentation
    • Expanded container image matrix and end-to-end CI/CD/GitOps documentation for the Thailand deployment flow.

sangwa and others added 3 commits June 19, 2026 11:20
Rework the image pipelines so semver releases promote the exact CI-built
(and staging-tested) image instead of rebuilding, and so path scoping works
without breaking tag handling.

Workflows:
- build.yml (default image): main push (path-scoped to image inputs) +
  dispatch. No tag trigger, so `paths:` filtering is reliable. Pushes
  sha-<short> always, latest on main, <branch-slug> on dispatch-from-branch.
- build-thailand.yml (overlay): workflow_run (overlay the fresh sha base,
  mirror its moving tag) + thai-path push (overlay latest, with a
  dorny/paths-filter guard that defers to workflow_run when base paths also
  changed, killing the double-build + stale-base overlay) + dispatch. Dropped
  the unreliable workflow_run.branches filter; gate on conclusion == success.
- release.yml (new): vX.Y.Z tag -> promote sha-<short> to X.Y.Z via `oras tag`
  (byte-identical to the tested image, immune to floating-pin drift), with a
  full build only as fallback. Thai mirrors the regular image's mode so its
  overlay always sits on the exact regular image being released.

GitOps compatibility: every producing job captures the image digest and emits
a short-lived (retention-days: 7) deploy-info artifact (image/tag/digest/
git_sha/ref/environment) for the forthcoming staging/production/siam-ai deploy
workflows. Builds set provenance:false / sbom:false for one stable
single-platform digest. concurrency added on all three (cancel-in-progress on
branch builds; never cancel a release).

Hardening:
- Add .dockerignore (shared by both Dockerfiles; excludes only non-image paths,
  keeps skills-thailand/ and the Dockerfiles).
- Pin every external action to an immutable commit SHA with a version comment.
  New actions bumped to latest major after a compatibility review:
  upload-artifact v7.0.1, paths-filter v4.0.1, setup-oras v2.0.0. Pre-existing
  actions held at latest-within-major for a follow-up phase.

Docs: rewrite CLAUDE.md CI/versioning/deployment sections to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-major bumps for the six actions that predated the pipeline rework,
after reviewing each major's release notes for breaking changes against our
usage. All re-pinned to immutable commit SHAs with version comments.

- actions/checkout            v4 -> v7.0.0
- actions/setup-node          v4 -> v6.4.0
- actions/setup-python        v5 -> v6.2.0
- docker/build-push-action    v6 -> v7.2.0
- docker/setup-buildx-action  v3 -> v4.1.0
- docker/login-action         v3 -> v4.2.0

Scrutiny notes:
- All bump to a Node-24 runtime requiring Actions Runner >= v2.327.1;
  satisfied by GitHub-hosted ubuntu-latest runners.
- checkout v7 (#2454) refuses to check out fork-PR code on workflow_run /
  pull_request_target. Verified harmless: the guard only fires when
  workflow_run.event starts with "pull_request" AND the head repo is a fork;
  our "Build and Push" triggers only on push/workflow_dispatch, so the check
  no-ops. It actually hardens our workflow_run+ref checkout.
- login v4 / setup-buildx v4 / build-push v7 only remove deprecated
  inputs/outputs and DOCKER_BUILD_* envs we don't use; our with: keys are
  core and unchanged.
- setup-node v5+ auto-enables package-manager caching when package.json has a
  packageManager field. The repo has no package.json/lockfile and the test job
  installs no JS deps, so this is inert; added an explicit
  package-manager-cache: false guard anyway.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add deploy-thailand.yml, which pins the agent-sandbox-thailand image in
EternisAI/gitops-siam-ai (apps/clusters/oa1/axion/app/release/values.yaml,
keys axion.sandbox.image.tag/.digest) so Flux reconciles it onto oa1-siam.

Triggers:
  - workflow_run after "Release (promote tag)" succeeds -> deploy the released
    semver, read from that run's deploy-info-siam-ai artifact.
  - workflow_dispatch -> deploy inputs.image_tag, else the branch's moving tag
    (latest on main, <slug> otherwise).

The digest is always re-resolved from GHCR via oras (registry is the source of
truth) and the run fails if the tag isn't published. The values.yaml edit is a
surgical awk line-replace anchored on the sandbox image's repository: line (not
yq, which would reflow the human-maintained file's comments/alignment); it is
idempotent and pushed via a rebase-retry loop. Runs in the "Axion Siam.AI"
GitHub Environment (GITOPS_PAT secret). No reconcile-watch (out of scope).

Update CLAUDE.md: Deployment section now documents deploy-thailand.yml as
implemented (siam-ai done; staging/production still forthcoming).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 613fdf12-10bd-4d19-9bfd-4062cf2f48dc

📥 Commits

Reviewing files that changed from the base of the PR and between 69a4b25 and 8a40e40.

📒 Files selected for processing (2)
  • .github/workflows/build-thailand.yml
  • .github/workflows/build.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/build.yml
  • .github/workflows/build-thailand.yml

Walkthrough

This PR overhauls the container image CI/CD pipeline. It adds a shared .dockerignore, rewrites build.yml to use SHA-pinned actions and a custom tag-computation shell step (producing sha-<short> + moving tags and a deploy-info.json artifact), and rewrites build-thailand.yml with path-filtered triggers, a dorny/paths-filter-based skip gate, and equivalent tag resolution. Two new workflows are added: release.yml promotes CI-built images to semver tags via ORAS retagging or fallback rebuild, and deploy-thailand.yml pins the Thailand image digest into a GitOps values.yaml with a retry-on-conflict push loop. test.yml action references are pinned to commit SHAs, and CLAUDE.md is updated to document the full pipeline.

Sequence Diagram

sequenceDiagram
  participant Dev as Developer
  participant BuildBase as build.yml
  participant BuildThai as build-thailand.yml
  participant Release as release.yml
  participant DeployThai as deploy-thailand.yml
  participant GHCR as GHCR Registry
  participant GitOps as gitops-siam-ai

  Dev->>BuildBase: push to main (Dockerfile)
  BuildBase->>GHCR: push base:sha-short + base:latest
  BuildBase->>BuildThai: trigger workflow_run
  
  BuildThai->>GHCR: build and push thai:sha-short + thai:latest

  Dev->>Release: push vX.Y.Z tag
  Release->>GHCR: oras check base:sha-short
  GHCR-->>Release: digest
  Release->>GHCR: oras tag → base:X.Y.Z
  Release->>GHCR: oras check thai:sha-short
  GHCR-->>Release: digest
  Release->>GHCR: oras tag → thai:X.Y.Z
  Release->>DeployThai: trigger workflow_run with artifacts

  DeployThai->>GHCR: oras resolve thai:X.Y.Z digest
  GHCR-->>DeployThai: image_digest
  DeployThai->>GitOps: checkout and awk update values.yaml
  GitOps-->>DeployThai: commit/push with retry loop
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • EternisAI/agent-sandbox#31: Directly introduces Dockerfile.thailand and the original build-thailand.yml, which this PR rewrites and extends with a full promotion and deploy pipeline.

Poem

🐇 Hoppity-hop through the pipeline I go,
SHA tags and digests in a tidy row,
oras retags what the builder has blessed,
awk patches YAML in the GitOps nest,
Five retries for pushes that land with a thud —
The Thailand deploy bunny seals it in mud! 🌸

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: CI/CD pipeline reworking and Thailand GitOps deployment addition.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/build-release-pipelines

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 100-103: The workflow is publishing the `:latest` tag from a
release fallback build, which can unintentionally move the latest tag away from
branch CI semantics. Remove the line containing `${{ steps.meta.outputs.image
}}:latest` from the tags list in the workflow, keeping only the short SHA-based
tag and the versioned tag to ensure the latest tag is only managed by the
primary branch CI pipeline.
- Around line 24-27: The tag trigger pattern in the `on.push.tags` section is
using regex syntax instead of glob syntax. Change the pattern
`v[0-9]+.[0-9]+.[0-9]+` to `v*.*.*` which is the correct glob pattern syntax
that will properly match semantic version tags like `v1.2.3`. The glob pattern
uses asterisks as wildcards rather than regex character classes and quantifiers.

In @.github/workflows/test.yml:
- Around line 15-17: The checkout steps in the test jobs are not disabling
credential persistence, which leaves the GitHub token in git config and widens
the security blast radius. Add the persist-credentials parameter set to false in
both checkout actions (the ones using
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 at lines 15-17 and the
corresponding step around lines 44-46) to prevent credential persistence since
these test jobs do not require repository write access.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b80e1fb6-9a4e-45c8-b571-eda14ec567b0

📥 Commits

Reviewing files that changed from the base of the PR and between 3b304c9 and 73c831f.

📒 Files selected for processing (8)
  • .dockerignore
  • .github/workflows/build-thailand.yml
  • .github/workflows/build.yml
  • .github/workflows/deploy-thailand.yml
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • CLAUDE.md
  • Dockerfile.thailand

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/test.yml
sangwa and others added 2 commits June 19, 2026 13:05
…st checkouts

- release.yml: the fallback build no longer pushes :latest. The moving `latest`
  tag is build.yml's (main CI) to manage; a release must not move it (the tagged
  commit may not be main's tip). Now mirrors the promote path, which only adds
  the :<ver> tag. Keeps sha-<short> + <ver>.
- test.yml: set persist-credentials: false on both checkouts — these jobs never
  push via git, so GITHUB_TOKEN need not persist in .git/config.

Skipped the reviewer's third suggestion (change tag filter to v*.*.*): the
current `v[0-9]+.[0-9]+.[0-9]+` is valid GitHub filter-pattern glob ([] ranges
and + quantifier are supported, . is literal) and is strictly more precise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dispatching build.yml or build-thailand.yml with a tag ref (e.g. v0.3.0)
selected would derive the output tag from the ref — building a v-prefixed slug
(agent-sandbox[-thailand]:v0.3.0) that overlays the wrong base (latest, not the
released version), uses the non-canonical `v` prefix, and bypasses release.yml's
byte-identical promote of sha-<short> -> <ver>.

Add a GITHUB_REF_TYPE=tag guard that fails fast with a pointer to the correct
path: push a vX.Y.Z git tag -> release.yml promotes both images. Restore the
semver mention to build-thailand.yml's workflow_dispatch comment + input
description.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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