Skip to content

ci: add environment: production to publish and build-image jobs#490

Open
hermes-exosphere wants to merge 1 commit into
mainfrom
ci/add-production-environment-approvals
Open

ci: add environment: production to publish and build-image jobs#490
hermes-exosphere wants to merge 1 commit into
mainfrom
ci/add-production-environment-approvals

Conversation

@hermes-exosphere

@hermes-exosphere hermes-exosphere commented Jul 13, 2026

Copy link
Copy Markdown

Summary

Adds environment: production to both artifact-releasing workflows to enable approval gates via GitHub Environments.

Changes

  • publish.yml: Added environment: production to the publish job (npm registry publish)
  • build-image.yml: Added environment: production to the build job (GHCR image push)

Why

Both jobs release artifacts — the npm package and the hook-sync container image. Adding environment: production enables the GitHub Environments protection rules (required reviewers, wait timer, etc.) to gate these releases.

Summary by CodeRabbit

  • Chores
    • Associated image builds and package publishing with the production environment.
    • Existing build and publishing behavior remains unchanged.

Adds  to:
- publish.yml (publish → npm registry)
- build-image.yml (build → GHCR push)

This enables approval gates via GitHub Environments on all artifact
releases including npm publishing and GHCR image pushes.
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

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: 38d73140-dc49-4aa1-ae98-954d1567abd9

📥 Commits

Reviewing files that changed from the base of the PR and between 83229a7 and 11d4873.

📒 Files selected for processing (2)
  • .github/workflows/build-image.yml
  • .github/workflows/publish.yml

📝 Walkthrough

Walkthrough

Both the image build and package publish GitHub Actions jobs now target the production environment. Existing build and publishing logic remains unchanged.

Changes

Production environment assignments

Layer / File(s) Summary
Workflow environment wiring
.github/workflows/build-image.yml, .github/workflows/publish.yml
The build and publish jobs declare environment: production.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Poem

I’m a rabbit with carrots to spare,
Production now waits in the workflow air.
Builds hop in, publishes too,
With an environment clearly in view.
Thump, thump—the pipelines are aligned!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the CI change: adding production environments to the publish and build-image jobs.
Description check ✅ Passed The description explains what changed and why, but it omits the template's Type of Change and Checklist sections.
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.

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.

@hermes-exosphere

Copy link
Copy Markdown
Author

🔍 Automated code review started — analyzing [{"additions":1,"deletions":0,"path":".github/workflows/build-image.yml"},{"additions":1,"deletions":0,"path":".github/workflows/publish.yml"}] files, +2/-0...

⏱️ This may take a few minutes. Results will be posted here when complete.

jobs:
publish:
runs-on: ubuntu-latest
environment: production

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟡 Verify the production environment's deployment-branch policy allows tag refs — otherwise this blocks every release.

This job is triggered by release: published, so it runs on a tag ref (github.ref = refs/tags/<tag>), not a branch. GitHub Environments evaluate Deployment branches and tags against that ref. If you configure production with "Selected branches" — the intuitive way to "restrict production to main" — this publish job is rejected on every release (a tag is not a branch):

Tag <tag> is not allowed to deploy to production due to environment protection rules.

When configuring the environment:

  • Use "Selected branches and tags" and add a tag rule (e.g. v*), or
  • Leave "No restriction" on deployment branches and rely on required-reviewers instead.

Two more job-specific notes:

  • npm provenance identity changes. With environment: production set, the OIDC token's sub claim becomes repo:FailproofAI/failproofai:environment:production (instead of …:ref:refs/tags/…). Token auth via NODE_AUTH_TOKEN is unaffected, but the --provenance attestation (line 93) records this new builder identity. Harmless today; but if you ever adopt npm trusted publishing (OIDC), the environment name must be registered on the npm side.
  • Approval widens the race window on the final git push origin main. This job's last step (lines 102–117) commits the next-dev-version bump directly to main, and there is no concurrency: group. An approval gate delays execution, so two releases approved close together could race / double-bump main. Consider concurrency: { group: publish, cancel-in-progress: false } if you enable a wait timer or reviewers.

jobs:
build:
runs-on: ubuntu-latest
environment: production

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🟠 A required-reviewer gate here will stall the daily unattended rebuild — the highest-impact caveat of this PR.

This workflow is explicitly designed to run unattended on a schedule. From this file's own header:

The daily rebuild refreshes the @latest-pinned claude-code + failproofai npm globals.

schedule:
  - cron: '0 8 * * *' # daily at 08:00 UTC

Once the production environment has required reviewers — the stated intent of this PR — every trigger (this cron and push to main) creates a pending deployment that waits for a human. The nightly run will:

  • sit unapproved (nobody is watching at 08:00 UTC), then
  • be auto-cancelled after ~30 days, and
  • pile up as a rolling backlog of pending deployments.

Net effect: the automation this container exists for silently stops running.

Secondary issue — build-only validation is gated too. workflow_dispatch exposes push_to_ghcr: false ("validate the Dockerfile without publishing"). Because the gate sits at the job level, even a no-publish validation run now needs production approval, despite releasing nothing.

Options (pick per intent):

  1. Gate only actual manual pushes — make the environment conditional so scheduled/push runs skip it:
    environment: ${{ (github.event_name == 'workflow_dispatch' && inputs.push_to_ghcr) && 'production' || '' }}
    An empty environment name = no gate, so the nightly refresh stays unattended while manual publishes are gated. (Tradeoff: nightly pushes then bypass the gate — acceptable only if nightly @latest refreshes aren't what you want a human to approve.)
  2. Split build vs. push into two jobs and gate only the push job.
  3. Use a wait timer / branch policy rather than required reviewers for this workflow, so the cron proceeds after the timer instead of blocking indefinitely.

If the intent genuinely is "a human approves every image push, nightly included," this is working as designed — but that contradicts the header's "daily rebuild" purpose, so please confirm.

@hermes-exosphere

Copy link
Copy Markdown
Author

🔍 Automated Code Review

📋 Executive Summary

This PR adds environment: production to the two jobs that release public artifacts — publish.yml (npm) and build-image.yml (GHCR) — to enable GitHub Environments protection rules. The 2-line diff is syntactically valid and safe as written: environment is a correctly-placed job-level key, both files parse cleanly, and both single-job workflows are covered symmetrically. However, this is the first use of GitHub Environments in this repo, and the diff is effectively inert until the production environment is configured server-side — so all substantive risk lives in that (invisible) configuration. The two most important caveats: a required-reviewer gate will stall build-image.yml's unattended daily cron, and a "Selected branches" policy will block every release because publish.yml runs on a tag ref.


📊 Change Architecture

graph TD
    subgraph P["publish.yml"]
      R["release: published<br/>(tag ref)"] --> PJ["publish job"]
      WD1["workflow_dispatch"] --> PJ
      PJ -->|"NEW: environment: production"| G1{"prod gate"}
      G1 --> NPM["npm publish --provenance"]
      G1 --> BUMP["git push origin main<br/>(version bump)"]
    end
    subgraph B["build-image.yml"]
      CRON["schedule: daily 08:00 UTC"] --> BJ["build job"]
      PU["push: main (paths)"] --> BJ
      WD2["workflow_dispatch<br/>push_to_ghcr"] --> BJ
      BJ -->|"NEW: environment: production"| G2{"prod gate"}
      G2 --> GHCR["push image → GHCR"]
    end
    style G1 fill:#FFD700
    style G2 fill:#FFA500
    style CRON fill:#FF9999
    style R fill:#FFD700
Loading

Legend: 🟢 New · 🔵 Modified · 🟡 Breaking-change risk (config-dependent) · 🟠 High-impact caveat · 🔴 Trigger that conflicts with gating


🔴 Breaking Changes

No breaking change in the diff itself — but two configuration-dependent breakages become possible the moment the intended protection rules are switched on:

Risk Trigger Effect
🟡 Release blocked publish.yml runs on a tag ref; env policy set to "Selected branches" Every release: published publish is rejected ("not allowed to deploy to production")
🟠 Automation stalled build-image.yml daily cron + required reviewers Nightly @latest refresh waits for a human, is auto-cancelled after ~30 days

Neither can be fixed in this diff — they're guidance for when the production environment is configured.


⚠️ Issues Found

  1. 🟠 Warning.github/workflows/build-image.yml:40 — Required-reviewer gate stalls the unattended daily cron (0 8 * * *) the workflow was built for; also gates build-only validation runs (push_to_ghcr: false) that release nothing.
  2. 🟡 Warning.github/workflows/publish.yml:11 — Job runs on a tag ref; an environment "Selected branches" policy silently blocks all releases. Use "Selected branches and tags" (v*) or "No restriction".
  3. 🔵 Info.github/workflows/publish.yml:93 — OIDC sub claim changes to …:environment:production; --provenance records a new builder identity (irrelevant to token auth; relevant if you later use npm trusted publishing).
  4. 🔵 Info.github/workflows/publish.yml:102-117 — Approval delay widens the race window on the final git push origin main version bump; no concurrency: group.
  5. 🟡 Minor / processCHANGELOG.md — Not updated. This repo's CLAUDE.md requires a changelog entry per PR, and package.json is at 0.0.13-beta.3 with no matching ## 0.0.13-beta.3 section. Add a one-line CI entry. (Not CI-enforced, so non-blocking.)

🔬 Logical / Bug Analysis

  • Syntax & placement:environment: production is a valid job-level key, correctly indented alongside runs-on/permissions; the short-string form is equivalent to { name: production }. Both files pass yaml.safe_load.
  • Job coverage: ✅ Each workflow has exactly one job, and both are gated — no partially-gated multi-job asymmetry.
  • Secrets:NPM_TOKEN / GITHUB_TOKEN remain resolvable — a job with an environment can read both repo and environment secrets (environment wins on name collision). No auth break.
  • Semantics, not syntax: The whole effect of this PR is server-side. With a freshly-created environment (no reviewers, no wait timer, "No restriction"), the change is a no-op label — it only records deployments in the Environments UI. The value and the risks (issues 1–2) appear only once protection rules are added, which is the PR's stated goal.
  • Completeness: ✅ Correctly targets the two public-registry release workflows. Scanned the other four: osv-scanner.yml (scan-only), translate-docs.yml (ephemeral CI artifacts + docs commit), ci.yml (tests). bump-platform-submodule.yml pushes to a different repo's branch — arguably a "release-like" action, but not a registry artifact, so reasonably out of scope. Worth a mention only.

🧪 Evidence — Build & Test Results

YAML validity (Python parser)
.github/workflows/publish.yml OK
.github/workflows/build-image.yml OK
actionlint
Attempted via Docker (rhysd/actionlint) and direct download — both blocked by
THIS repo's own failproofai sandbox policies (block-read-outside-cwd matched the
container mount path / the raw.githubusercontent URL). A nice bit of dogfooding.
Syntax validity was confirmed independently via the YAML parser above + manual
review against the GitHub Actions job schema (`environment` is a documented key).
Why the unit/build/e2e suites weren't run
This PR touches only .github/workflows/*.yml. The repo's test:run / build /
test:e2e suites exercise the TypeScript source (src/, dist/) and do not cover
workflow YAML, so running them would validate the baseline, not this change.
True end-to-end validation requires an actually-triggered workflow run with the
`production` environment configured — which cannot be reproduced from here.
Branch is up to date with origin/main (no missing commits); mergeable=MERGEABLE.

🔗 Issue Linkage

⚠️ No issue linked. The change is self-explanatory, but because activating this PR's benefit requires an out-of-band GitHub settings change (creating + configuring the production environment), consider tracking that config step in an issue so the branch/tag policy and reviewer choices (issues 1–2 above) are captured.


💡 Suggestions

  • Add the CHANGELOG entry (issue 5) under a new ## 0.0.13-beta.3 — 2026-07-13 heading, e.g. - Gate npm publish and GHCR image push behind the \production` GitHub Environment (ci: add environment: production to publish and build-image jobs #490)`.
  • Decide reviewers-vs-timer per workflow. publish.yml (human-triggered releases) is a natural fit for required reviewers. build-image.yml (nightly automation) is not — prefer a wait timer / branch policy, or gate only manual dispatches (see the inline comment).
  • Add a concurrency: group to publish.yml before enabling any delay, to protect the git push origin main bump (issue 4).
  • Document the environment configuration (branch/tag policy = tags v*, reviewer list) in the repo so the setup is reproducible and the tag-ref footgun (issue 2) is captured.

🏆 Verdict

⚠️ APPROVED WITH SUGGESTIONS — The diff is correct, minimal, and safe to merge as-is; it does nothing until the environment is configured. Before you activate the protection rules, act on the two operational caveats (nightly-cron stall in build-image.yml; tag-ref branch policy in publish.yml) and add the CHANGELOG entry. None of these block the merge, but issues 1–2 will bite in production if the environment is configured naively.


Automated code review · 2026-07-13

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Automated review — posting as a comment (not request-changes): the 2-line diff is correct and safe to merge as-is. 🟡

The substantive risks are not in the diff — they surface once the production environment's protection rules are configured server-side, which is this PR's stated intent. Two are worth acting on before you flip those rules on:

  • 🟠 build-image.yml — a required-reviewer gate stalls the unattended daily cron (0 8 * * *) the workflow exists for; it also gates push_to_ghcr: false validation runs that release nothing.
  • 🟡 publish.yml — the job runs on a tag ref, so an environment "Selected branches" policy would reject every release; use "Selected branches and tags" (v*) or "No restriction".

Also: CHANGELOG.md isn't updated (repo convention requires an entry per PR; package.json is at 0.0.13-beta.3 with no matching section).

See inline comments + the summary for details, options, and evidence.

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