Skip to content

feat(ci): rainix-tag-release — tag-triggered deploy-repo release - #280

Merged
thedavidmeister merged 3 commits into
mainfrom
tag-triggered-release
Jul 24, 2026
Merged

feat(ci): rainix-tag-release — tag-triggered deploy-repo release#280
thedavidmeister merged 3 commits into
mainfrom
tag-triggered-release

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Why

Deploy repos and library repos need mutually exclusive release lifecycles, and today st0x.deploy (a deploy repo) is wrongly on the library one. The symptom is testDeployTag, red on st0x.deploy main for weeks — I watched it climb 0_1_8 != 0_1_9 → 0_1_26 → 0_1_27 → 0_1_28 → 0_1_29 across a single day of merges.

That test compares generated LibProdDeployCurrent.DEPLOY_TAG (the frozen snapshot's version) against foundry.toml's [package].version. Under rainix-autopublish's next-version lifecycle, [package].version is the next, unpublished version — deliberately one ahead of the registry, bumped on every content-changing merge. DEPLOY_TAG only advances when a deploy regenerates the pins. So the two are designed to differ; the test is green only in the instant after a regeneration and red on main between every merge and the next deploy.

A permanently-red check is worse than a missing one — it trains everyone to wave CI through. This PR gives deploy repos their own lifecycle where version and snapshot move only together, at release time.

The two lifecycles

Library repo (rainix-autopublish) Deploy repo (rainix-tag-release, this PR)
Trigger push to main push a release tag
[package].version next, unpublished (ahead of registry) last released; moves only at release
Per-tag snapshot none (imports abstract surface only) src/generated/<tag>/ pins, frozen + append-only
On merge content gate → publish → bump to next nothing

A repo is strictly one or the other.

What a release does

Nothing on merge — a PR lands source only, main stays at the last release (its live contracts still match its pins, so the fork suite stays green). A human tags sol-v<X>, and this workflow runs deploy + publish + snapshot as one act:

deploy job — broadcasts every suite in dependency order by fanning the existing rainix-manual-sol-artifacts reusable over deploy-suites at max-parallel: 1 (one forge run per suite — the Zoltu nonce isolation the manual dispatch already enforces). Composes the deploy reusable rather than re-implementing its broadcast/env.

release job (needs: deploy) — so publish/snapshot only run once the chain carries the code:

  1. Sets foundry.toml version to <X> (from the tag).
  2. Regenerates the deterministic snapshot — address = f(bytecode) under CREATE2, no chain access — via snapshot-generate-cmd (e.g. forge script ./script/BuildPointers.sol && forge fmt).
  3. Commits it, then enforces append-only (frozen-snapshots-append-only): a release only adds src/generated/<X>/, never edits a frozen tag.
  4. Verifies the live chain matches the fresh pins (the repo's fork suite) — confirms the deploy landed; a snapshot of addresses the chain doesn't carry fails loud before publishing.
  5. Publishes to Soldeer at <X>.
  6. Commits the snapshot back to main, so the daily drift sweep has the current release's pins.
  7. Cuts the GitHub release.

testDeployTag then holds by construction: version and DEPLOY_TAG advance only in the same commit.

Design decisions

Sibling, not a mode on rainix-autopublishdecided. The lifecycles share ~40% of steps (deploy-key checkout, nix setup, git config, Soldeer push, GH release) — all already in composite actions this reuses — and differ on the other ~60% (content-gate + next-version bump vs. deploy + snapshot-regen + append-only + commit-back). Guarding every autopublish step with a publish-trigger conditional would put two lifecycles in one file that float, raindex, and other critical packages depend on for their releases, where a subtle conditional error breaks a real publish. A sibling has zero blast radius; autopublish is untouched.

Manual git tag is the triggerdecided. The human pushes sol-v<X>; that fires the release. The tag ref points at the tagged source commit, while the release snapshot lands on main (a commit above it) and in the published Soldeer package — which is the canonical distribution consumers pin. No workflow_dispatch/tag-moving; the tag is exactly where the human put it.

Deploy composes the reusable, order enforced two ways. The deploy job is a matrix over rainix-manual-sol-artifacts (single source of truth for how a suite broadcasts). Sequencing rests on max-parallel: 1 running the suites in listed order and the on-chain dep-codehash check, which fails loud if a dependent suite somehow runs before its dependency — so an ordering slip is a loud re-runnable failure (Zoltu deploys are idempotent), never silent corruption.

Consumer change (follow-up PR on st0x.deploy, not here)

package-release.yaml flips from on: push: branches + rainix-autopublish to:

on: { push: { tags: ['sol-v*'] } }
jobs:
  release:
    uses: rainlanguage/rainix/.github/workflows/rainix-tag-release.yaml@main
    with:
      soldeer-package: st0x-deploy
      snapshot-generate-cmd: forge script ./script/BuildPointers.sol && forge fmt
      deploy-suites: >-
        ["stox-receipt","stox-wrapped-token-vault","stox-wrapped-token-vault-beacon",
         "stox-wrapped-token-vault-beacon-set-deployer","stox-offchain-asset-receipt-vault-authorizer-v1",
         "stox-offchain-asset-receipt-vault-payment-mint-authorizer-v1","stox-corporate-actions-facet",
         "stox-receipt-vault","stox-offchain-asset-receipt-vault-beacon-set-deployer","stox-unified-deployer",
         "st0x-orchestrator","st0x-orchestrator-beacon-set-deployer"]
    secrets:
      PUBLISH_PRIVATE_KEY: ${{ secrets.PUBLISH_PRIVATE_KEY }}
      PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
      SOLDEER_API_TOKEN: ${{ secrets.SOLDEER_API_TOKEN }}
      RPC_URL_BASE_FORK: ${{ secrets.RPC_URL_BASE_FORK }}
      RPC_URL_ETHEREUM_FORK: ${{ secrets.RPC_URL_ETHEREUM_FORK }}
      # …the fork RPCs the verify gate + deploy need, plus Etherscan keys

(The suite list is lifted verbatim from st0x.deploy's current manual-sol-artifacts.yaml dispatch choices, already in dependency order.)

Validation

  • YAML parses; job graph is deploy (matrix, max-parallel 1, uses: rainix-manual-sol-artifacts) → release (needs: deploy); no duplicate keys.
  • Every composite/reusable input verified against its action.yml / reusable on: (checkout ssh-key, nix-cachix-setup, gh-release, frozen-snapshots-append-only, rainix-manual-sol-artifacts suite/script/verify).
  • rainix pre-commit clean (yamlfmt, no-consumer-prettier).
  • Not exercised end-to-end: needs the st0x.deploy consumer flip and a real release tag, and the fork verify gate is currently blocked org-wide by a disabled Ankr RPC key (separate infra issue), so the consumer PR waits on that regardless.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a reusable release workflow for tag-based deployments.
    • Validates release tags and confirms they are based on the selected main branch.
    • Deploys configured suites sequentially before publishing a release.
    • Updates package versions and deployment snapshots automatically.
    • Verifies deployed networks and publishes packages to Soldeer.
    • Creates a GitHub Release and updates the main branch with release changes.

Adds the deploy-repo counterpart to rainix-autopublish. Deploy repos run a
frozen-snapshot lifecycle that is mutually exclusive with autopublish's
next-version lifecycle; running the latter on a deploy repo desyncs
[package].version from DEPLOY_TAG on every merge, leaving a permanently-red
identity test (testDeployTag) that trains reviewers to ignore CI.

Here nothing moves on merge. A human tag is the sole release trigger: the
workflow sets the version from the tag, regenerates the deterministic
deploy-pin snapshot, enforces append-only, verifies the live chain against
the fresh pins, publishes to Soldeer, and commits the snapshot back to main
so the drift sweep tracks the current release.

Reuses the checkout / nix-cachix-setup / frozen-snapshots-append-only /
gh-release composites. A sibling rather than a mode on rainix-autopublish,
to keep zero blast radius on the library repos that depend on it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a3d2bcd5-af45-42b8-8fe1-2c8066c05178

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Tag release workflow

Layer / File(s) Summary
Workflow contract and tag validation
.github/workflows/rainix-tag-release.yaml
Defines callable deployment and release inputs plus secrets, and verifies that the triggering tag points to a commit merged into the selected main branch.
Sequential deployment orchestration
.github/workflows/rainix-tag-release.yaml
Runs the configured deployment suites sequentially through rainix-manual-sol-artifacts, passing deployment scripts, verification settings, and network credentials.
Versioned snapshot generation and verification
.github/workflows/rainix-tag-release.yaml
Prepares the release repository, validates the tag version, updates foundry.toml, regenerates deploy-pin snapshots, enforces append-only behavior, and runs live-chain tests.
Package publication and release finalization
.github/workflows/rainix-tag-release.yaml
Publishes the package to Soldeer, rebases and pushes the release commit onto the selected branch, and creates a GitHub Release.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Guard
  participant DeployJob
  participant ReleaseJob
  participant GitRepository
  participant LiveChain
  participant Soldeer
  participant GitHubRelease
  Caller->>Guard: invoke workflow with tag inputs and secrets
  Guard->>GitRepository: verify tag commit is merged into main-branch
  Guard->>DeployJob: allow deployment
  DeployJob->>ReleaseJob: complete sequential deploy suites
  ReleaseJob->>GitRepository: derive VERSION and update package version
  ReleaseJob->>GitRepository: regenerate and commit deploy-pin snapshot
  ReleaseJob->>LiveChain: run configured verification tests
  LiveChain-->>ReleaseJob: return verification result
  ReleaseJob->>Soldeer: publish package as soldeer-package~VERSION
  ReleaseJob->>GitRepository: rebase onto main-branch and push
  ReleaseJob->>GitHubRelease: create release for github.ref_name
Loading

Possibly related PRs

🚥 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 clearly summarizes the main change: a CI workflow for tag-triggered deploy-repo releases.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tag-triggered-release

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.

@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: 7

🤖 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/rainix-tag-release.yaml:
- Around line 113-116: Strengthen the tag guard in the release workflow by
fetching the selected release branch and verifying that the tag commit is an
ancestor of that branch before generation or publishing. Update the guard around
“Guard - the trigger ref is a tag” to reject tags from unmerged branches while
preserving the existing refs/tags validation.
- Around line 157-165: Remove the broad git checkout from the “Commit the
release snapshot” step so it does not discard the foundry.toml version update or
tracked snapshot artifacts. Replace it with cleanup targeting only explicitly
known temporary devShell leftovers, then retain git add -A so the regenerated
release files and version changes are staged together.
- Around line 187-209: The release workflow’s Soldeer publication and main
commit-back are not resumable across partial failures. Update the release
finalization steps around “Publish to Soldeer” and “Commit the snapshot back to
main” to persist durable release state, record whether publication completed,
and on retry skip or safely reconcile an already-published package before
retrying the rebase/push; ensure failures leave enough state for idempotent
recovery without republishing non-idempotently.
- Around line 131-151: Update the release-version step to validate VERSION
against the repository’s supported package version format, rejecting arbitrary
suffixes such as sol-vfoo before exporting it. In the foundry.toml update step,
safely pass the validated VERSION into sed so replacement characters cannot
alter the result, and verify that the first package version line was actually
updated, failing the workflow otherwise. Anchor changes to the VERSION parsing
block and the “Set foundry.toml version to the release version” step.
- Around line 77-78: Update the SOLDEER_API_TOKEN workflow input configuration
to set required to true, ensuring callers provide the secret before the release
workflow performs preparation and invokes forge soldeer push.
- Around line 98-100: Remove the unused id-token permission from the workflow
permissions block, retaining contents: write for the pinned rainix/gh-release
composite and leaving the rest of the release workflow unchanged.
- Line 118: Update the tag-trigger validation step around the error echo to pass
github.ref through an environment variable, then reference that variable with a
quoted "%s" format in the shell command. Remove direct github.ref interpolation
from the generated script while preserving the existing error message and
behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f0faa7f-9958-41ec-8a6c-ec0fb20fa4dd

📥 Commits

Reviewing files that changed from the base of the PR and between 409759f and 795d4b8.

📒 Files selected for processing (1)
  • .github/workflows/rainix-tag-release.yaml

Comment thread .github/workflows/rainix-tag-release.yaml Outdated
Comment thread .github/workflows/rainix-tag-release.yaml
Comment thread .github/workflows/rainix-tag-release.yaml Outdated
# sense for the release tags the caller's `on: push: tags` filter allows.
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: |
echo "::error::rainix-tag-release must be triggered by a tag push, got ${{ github.ref }}" >&2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## workflow excerpt"
if [ -f .github/workflows/rainix-tag-release.yaml ]; then
  nl -ba .github/workflows/rainix-tag-release.yaml | sed -n '90,130p'
else
  echo "missing .github/workflows/rainix-tag-release.yaml"
fi

echo
echo "## all github.ref interpolations in this workflow"
rg -n '\$\{\{\s*github\.ref\s*\}\}' .github/workflows/rainix-tag-release.yaml || true

echo
echo "## repository workflow files under .github/workflows"
git ls-files '.github/workflows/*' | sed -n '1,120p'

Repository: rainlanguage/rainix

Length of output: 217


🌐 Web query:

GitHub Actions avoid injection of untrusted values ref branch-name run shell command

💡 Result:

To avoid shell command injection when using untrusted values like branch names (e.g., github.head_ref) in GitHub Actions run commands, you must never directly interpolate these values into your script using the ${{ }} syntax [1][2][3]. The ${{ }} syntax is evaluated by the GitHub Actions runner before the script is executed, effectively splicing the untrusted input directly into your code [4][2]. If a branch name contains shell metacharacters (e.g., ";", "&", "|"), an attacker can use them to break out of the intended command and execute arbitrary code on the runner [1][5][2]. The industry-standard solution is to use intermediate environment variables [6][4][7]. By assigning the untrusted input to an environment variable, you ensure the value is passed to the shell as a literal string rather than being interpreted as part of the command structure [2][8]. Recommended implementation: 1. Define an environment variable in the step's env block [6][8]. 2. Use the shell's native variable expansion syntax (e.g., "$VAR") within the run block to reference it [6][2][8]. Example of secure usage: - name: Process branch name env: BRANCH_NAME: ${{ github.head_ref }} run: | # Use the variable in quotes to prevent word splitting and command injection echo "Processing branch: $BRANCH_NAME" git checkout "$BRANCH_NAME" Always treat values from the github context—particularly those ending in ref, name, message, title, or body—as untrusted input [1][3]. In addition to using environment variables, ensure that your environment variables are always double-quoted within your scripts to prevent word splitting vulnerabilities [3][7]. Avoiding the use of risky triggers like pull_request_target on untrusted forks further helps mitigate the impact if an injection vulnerability exists [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## workflow excerpt"
if [ -f .github/workflows/rainix-tag-release.yaml ]; then
  awk '{printf "%5d\t%s\n", NR, $0}' .github/workflows/rainix-tag-release.yaml | sed -n '100,125p'
else
  echo "missing .github/workflows/rainix-tag-release.yaml"
fi

echo
echo "## all github.ref interpolations in this workflow"
grep -nE '\$\{\{\s*github\.ref\s*\}\}' .github/workflows/rainix-tag-release.yaml || true

echo
echo "## repository workflow files under .github/workflows"
git ls-files '.github/workflows/*'

Repository: rainlanguage/rainix

Length of output: 2682


Pass github.ref through an environment variable.

Direct $\$\{\{ github.ref \}\} interpolation in run exposes refs/tags/.../branch refs to the generated shell script. Use an intermediate env var and "%s" so shell-significant ref names cannot alter the command.

Suggested fix
       - name: Guard - the trigger ref is a tag
+        env:
+          TRIGGER_REF: ${{ github.ref }}
         ...
         run: |
-          echo "::error::rainix-tag-release must be triggered by a tag push, got ${{ github.ref }}" >&2
+          printf '::error::rainix-tag-release must be triggered by a tag push, got %s\n' "$TRIGGER_REF" >&2
           exit 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo "::error::rainix-tag-release must be triggered by a tag push, got ${{ github.ref }}" >&2
- name: Guard - the trigger ref is a tag
env:
TRIGGER_REF: ${{ github.ref }}
...
run: |
printf '::error::rainix-tag-release must be triggered by a tag push, got %s\n' "$TRIGGER_REF" >&2
exit 1
🧰 Tools
🪛 zizmor (1.26.1)

[error] 118-118: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for 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.

In @.github/workflows/rainix-tag-release.yaml at line 118, Update the
tag-trigger validation step around the error echo to pass github.ref through an
environment variable, then reference that variable with a quoted "%s" format in
the shell command. Remove direct github.ref interpolation from the generated
script while preserving the existing error message and behavior.

Source: Linters/SAST tools

Comment thread .github/workflows/rainix-tag-release.yaml Outdated
Comment thread .github/workflows/rainix-tag-release.yaml
Comment thread .github/workflows/rainix-tag-release.yaml
Add a `deploy` job that fans rainix-manual-sol-artifacts over `deploy-suites`
in dependency order (max-parallel 1, one forge run per suite for Zoltu nonce
isolation); the `release` job now `needs:` it, so publish/snapshot run only
once the chain carries the code the pins name. Composes the existing deploy
reusable rather than re-implementing its broadcast/env.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…validation

- guard job: require the release tag to be an ancestor of the release branch
  before deploy/publish, so a tag from an unmerged branch can't rebase unreviewed
  commits onto main or broadcast unreviewed bytecode (CRITICAL). Refs via env,
  not github.ref interpolation.
- commit step: drop `git checkout -- .`, which reverted the foundry.toml version
  bump and regenerated tracked files, breaking version/snapshot lockstep (CRITICAL).
- validate VERSION as MAJOR.MINOR.PATCH: rejects junk tags and removes any
  sed-special char before the foundry.toml edit; fail if the version line is absent.
- SOLDEER_API_TOKEN required:true (always publishes); drop unused id-token perm.

Resumability of publish-then-commit-back is a known, documented limitation left as
follow-up (heavy lift; needs durable release state).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit findings in f1fa5a9:

  • 🔴 Tag provenance (was L188): new guard job requires the tagged commit to be an ancestor of the release branch (git merge-base --is-ancestor "$GITHUB_SHA" origin/$MAIN) before deploy or release runs — a tag cut from an unmerged branch is now rejected, so the rebase can't replay unreviewed commits onto main and the deploy can't broadcast unreviewed bytecode. All refs come from built-in env vars, not github.ref interpolation (🔒 was L190).
  • 🔴 Worktree restore (was L237): removed git checkout -- . from the commit step — it reverted the foundry.toml version bump and any regenerated tracked file, which would have broken the version/snapshot lockstep. The one devShell leftover is already hidden via .git/info/exclude.
  • 🟠 VERSION validation (was L223): the parsed version must match MAJOR.MINOR.PATCH; this rejects junk tags (sol-vfoo) and, since semver carries no &/\//, removes any sed-injection into foundry.toml. Also fails loud if no [package] version line was updated.
  • 🟠 SOLDEER_API_TOKEN (was L118): required: true — this workflow always publishes, so a missing token should fail at setup, not after the deploy.
  • 🔒 id-token (was L172): dropped the unused id-token: write; only contents: write remains (Soldeer uses SOLDEER_API_TOKEN, the release uses GITHUB_TOKEN).

Not fixed — documented follow-up: publish-then-commit-back resumability (was L281). If Soldeer publish succeeds but the push to main fails, the package is published without its snapshot on main. This is the atomicity limitation already noted in the PR description; a proper fix needs durable release state and is a heavy lift for a follow-up, not this PR.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

workflow=".github/workflows/rainix-tag-release.yaml"

printf '%s\n' '--- workflow outline ---'
ast-grep outline "$workflow" --items all || true

printf '%s\n' '--- current workflow with line numbers ---'
cat -n "$workflow"

printf '%s\n' '--- commit diff (f1fa5a9) ---'
git show --format=fuller --find-renames --find-copies -- "$workflow" f1fa5a9

printf '%s\n' '--- PR branch diff against merge base ---'
base="$(git merge-base HEAD origin/main 2>/dev/null || true)"
if [ -n "$base" ]; then
  git diff --no-ext-diff --unified=40 "$base"...HEAD -- "$workflow"
else
  git diff --no-ext-diff --unified=40 HEAD^..HEAD -- "$workflow"
fi

Length of output: 47604


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- locate and map checkout composite ---'
fd -a '^checkout\.ya?ml$' .github/actions | while IFS= read -r file; do
  echo "### $file"
  ast-grep outline "$file" --items all || true
  cat -n "$file"
done

printf '%s\n' '--- locate tag-release callers and comparable tag guards ---'
rg -n -C 8 --glob '*.yml' --glob '*.yaml' \
  '(rainix-tag-release|workflow_call:|refs/tags/|GITHUB_REF_NAME|merge-base --is-ancestor)' \
  .github

printf '%s\n' '--- workflow files and reusable-action declarations ---'
fd -a -e yaml -e yml .github/workflows .github/actions | sort

Length of output: 19586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- action metadata available on this PR branch ---'
git ls-tree -r --name-only HEAD -- .github/actions | \
  grep -E '(^|/)(action\.ya?ml|checkout\.ya?ml)$' || true

printf '%s\n' '--- checkout action metadata from repository default branch ---'
for path in .github/actions/checkout/action.yml .github/actions/checkout/action.yaml; do
  echo "### $path"
  gh api "repos/rainlanguage/rainix/contents/$path?ref=main" \
    --jq '.content' 2>/dev/null | base64 --decode 2>/dev/null || true
done

printf '%s\n' '--- YAML parse check ---'
ruby -ryaml -e '
  YAML.load_file(".github/workflows/rainix-tag-release.yaml")
  puts "YAML parsed successfully"
'

Length of output: 2392


@thedavidmeister Focused verification confirms the reported fixes are in place:

  • The shared checkout action uses the triggering event ref by default, so the guard’s GITHUB_SHA ancestry check evaluates the release tag’s commit.
  • guard → deploy → release correctly prevents deployment and publication when provenance validation fails.
  • The strict version validation and foundry.toml update check are present.
  • The generated version/snapshot changes are retained and staged together.
  • SOLDEER_API_TOKEN is required, and permissions are reduced to contents: write.
  • The workflow parses successfully as YAML.

The publish-then-commit-back atomicity limitation remains explicitly documented as a follow-up. No additional issue surfaced in this focused verification.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Reviewed f1fa5a9: approve

All 12 rainix checks green on this sha. The two CodeRabbit Criticals (tag provenance, worktree restore) and three Majors (VERSION validation, SOLDEER_API_TOKEN required, unused id-token) are fixed in f1fa5a9 — those threads are now outdated. The one remaining finding, publish/commit-back resumability, is a documented heavy-lift follow-up (a rare partial-failure recovery path), not a defect in what lands here; thread resolved with that note. Reviewed my own authored change adversarially: the guard job's ancestry check closes the unmerged-tag hole, and removing git checkout -- . restores the version/snapshot lockstep this PR exists to guarantee.

@thedavidmeister
thedavidmeister merged commit 06fa7b6 into main Jul 24, 2026
11 of 13 checks passed
@github-actions

Copy link
Copy Markdown

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- PR metadata and changed files ---'
gh pr view 280 --repo rainlanguage/rainix --json baseRefName,headRefName,commits,files \
  --jq '{base: .baseRefName, head: .headRefName, commitCount: (.commits | length), files: [.files[] | {path, additions, deletions}]}'

printf '%s\n' '--- merge-base diff statistics ---'
base="$(git merge-base HEAD "origin/$(gh pr view 280 --repo rainlanguage/rainix --json baseRefName --jq .baseRefName)")"
git diff --stat "$base"...HEAD
git diff --numstat "$base"...HEAD

printf '%s\n' '--- changed workflow ---'
workflow=".github/workflows/rainix-tag-release.yaml"
if [ -f "$workflow" ]; then
  cat -n "$workflow"
fi

Length of output: 21227


SIZE=L

@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: 1

🤖 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/rainix-tag-release.yaml:
- Around line 144-166: Add explicit least-privilege permissions to both the
guard and deploy jobs: declare contents read access for guard, and ensure deploy
passes only contents read access to the called rainix-manual-sol-artifacts.yaml
reusable workflow. Keep release’s existing contents write permission unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a8f5c21-886c-4cb3-86e7-c681bdf086cc

📥 Commits

Reviewing files that changed from the base of the PR and between 795d4b8 and f1fa5a9.

📒 Files selected for processing (1)
  • .github/workflows/rainix-tag-release.yaml

Comment on lines +144 to +166
guard:
runs-on: ubuntu-latest
steps:
- uses: rainlanguage/rainix/.github/actions/checkout@main
- name: Tag must be a tag on the release branch
env:
MAIN: ${{ inputs.main-branch }}
run: |
set -euo pipefail
case "$GITHUB_REF" in
refs/tags/*) : ;;
*) echo "::error::rainix-tag-release must be triggered by a tag push, got $GITHUB_REF" >&2; exit 1 ;;
esac
# The shared checkout is shallow; unshallow so the ancestry test can see
# whether the tag commit is on the release branch.
if [ -f "$(git rev-parse --git-dir)/shallow" ]; then
git fetch --no-tags --unshallow origin
fi
git fetch --no-tags origin "$MAIN"
if ! git merge-base --is-ancestor "$GITHUB_SHA" "origin/$MAIN"; then
echo "::error::tag $GITHUB_REF_NAME ($GITHUB_SHA) is not on origin/$MAIN — refusing to release an unmerged commit" >&2
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Declare explicit least-privilege permissions on guard and deploy.

Neither job declares a permissions: block, so both fall back to the runner/org default token permissions (flagged by zizmor for the deploy job, which also grants those defaults to the called rainix-manual-sol-artifacts.yaml reusable workflow). Only read access to repo contents is needed here; release already does this correctly with contents: write.

🔒 Proposed fix
   guard:
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:
   deploy:
     needs: guard
+    permissions:
+      contents: read
     strategy:
       max-parallel: 1

Also applies to: 172-198

🤖 Prompt for 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.

In @.github/workflows/rainix-tag-release.yaml around lines 144 - 166, Add
explicit least-privilege permissions to both the guard and deploy jobs: declare
contents read access for guard, and ensure deploy passes only contents read
access to the called rainix-manual-sol-artifacts.yaml reusable workflow. Keep
release’s existing contents write permission unchanged.

Source: Linters/SAST tools

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