Skip to content

feat(ci): split staging vs release workflows; release from staging tag#1096

Closed
senamakel wants to merge 1 commit into
tinyhumansai:mainfrom
senamakel:feat/split-staging-release-workflows-1089
Closed

feat(ci): split staging vs release workflows; release from staging tag#1096
senamakel wants to merge 1 commit into
tinyhumansai:mainfrom
senamakel:feat/split-staging-release-workflows-1089

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented May 2, 2026

Summary

Closes #1089. Splits the unified release.yml (which routed both production and staging through one build_target switch) into two first-class workflows with separate concurrency, permissions, and artifacts.

  • Staging (release-staging.yml): each successful cut now pushes an immutable staging/vX.Y.Z-N tag. Base X.Y.Z reads from app/package.json without mutation; N is a monotonic counter computed from existing staging tags (tag-only metadata, no version-bump commits land on main). A cleanup-failed-staging job drops the tag if the matrix fails so production never resolves to a half-built artifact.
  • Production (release.yml): now production-only. Replaces build_target with release_source (staging_tag default | main_head) plus an optional staging_tag pin. Empty pin = latest staging/v* by creatordate. The bump commit and release tag record source/ref/sha provenance. Production keeps patch/minor/major; default is minor.
  • Concurrency: release-staging and release-production-main are now distinct groups so neither blocks the other.
  • Helpers: new scripts/release/next-staging-tag.js and scripts/release/resolve-release-source.sh.
  • Docs: docs/RELEASE.md runbook covers both flows, tag conventions, rollback, and helper scripts. CLAUDE.md points at it.

Test plan

  • Trigger Release (Staging) from main; confirm a staging/v0.53.4-1 tag is pushed and matrix succeeds
  • Trigger Release (Staging) again; confirm counter increments to -2 and prior tag stays put
  • Trigger Release with release_source=staging_tag, staging_tag="", release_type=minor; confirm latest staging tag resolves, version bumps on main, vX.(Y+1).0 tag is pushed, and release ships
  • Trigger Release with release_source=main_head, release_type=patch; confirm the hotfix path works
  • Force-fail a staging matrix leg; confirm cleanup-failed-staging drops the tag
  • Verify scripts/release/resolve-release-source.sh errors clearly when no staging tags exist

Summary by CodeRabbit

  • Documentation

    • Added comprehensive release process documentation covering staging and production workflows, tag conventions, and rollback procedures.
    • Updated repository documentation with migration notes and refreshed layout information.
  • Chores

    • Improved release workflow automation with separate staging and production pipelines, including immutable tag creation and enhanced cleanup on failures.

tinyhumansai#1089)

Splits the unified `release.yml` (which routed both production and staging
through one `build_target` switch) into two first-class workflows:

- `release-staging.yml` now creates immutable `staging/vX.Y.Z-N` tags on
  every successful cut. The base `X.Y.Z` is read from `app/package.json`
  without mutation; `N` is a monotonic counter computed from existing
  staging tags. Tag-only metadata keeps cadence noise-free (no version-
  bump commits land on `main` from staging). A cleanup job drops the
  staging tag if the build matrix fails so production never resolves to
  a half-built artifact.

- `release.yml` is now production-only. Replaces the `build_target`
  switch with `release_source` (`staging_tag` default | `main_head`)
  plus an optional `staging_tag` pin. Empty pin = latest `staging/v*`
  tag by `creatordate`. The bump commit and release tag record source
  provenance in the message.

- Concurrency groups are split (`release-staging` vs
  `release-production-main`) so neither workflow blocks the other.

- New helpers: `scripts/release/next-staging-tag.js` (compute next
  staging tag) and `scripts/release/resolve-release-source.sh` (resolve
  release source ref + sha for traceability).

- Runbook: `docs/RELEASE.md` documents both flows, tag conventions,
  rollback rules, and helper scripts. `CLAUDE.md` points at it.

Closes tinyhumansai#1089
@senamakel senamakel requested a review from a team May 2, 2026 00:30
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

📝 Walkthrough

Walkthrough

The PR splits a unified release workflow into separate staging and production workflows, adds immutable staging Git tags on each cut, and introduces helper scripts to compute next staging tags and resolve release sources. Both workflows are documented with updated guidance in the codebase.

Changes

Release Workflow Refactoring

Layer / File(s) Summary
Helper Scripts
scripts/release/next-staging-tag.js, scripts/release/resolve-release-source.sh
New Node and Bash CLI scripts compute monotonic staging tags from app/package.json version (incrementing patch counter) and resolve production release sources from a staging tag (latest or pinned) or main HEAD with error handling.
Staging Workflow
.github/workflows/release-staging.yml
Redesigned prepare-build job runs from main, validates/creates/pushes immutable staging/vX.Y.Z-N tags, and outputs the tag name (not SHA) as build_ref. New cleanup-failed-staging job deletes the staging tag on matrix failure. Permissions upgraded to contents: write.
Production Workflow
.github/workflows/release.yml
Updated dispatch inputs to release_source and staging_tag (selecting staging tag or main as build source). Reworded prepare-build steps to resolve source via new script, embed source metadata into commits/tags, and always use release profile/production environment. Concurrency group fixed to release-production-main.
Release Documentation
docs/RELEASE.md
New comprehensive guide documents staging-tag creation (patch-only, no version bumps to main), production promotion (from staging tag or main, with minor/major semver bumps), tag conventions, failure cleanup, and helper script roles.
Developer Documentation
CLAUDE.md
Updated repository structure table, added Releases section describing staging vs production workflows, and clarified pnpm delegation from repo root.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • M3gA-Mind

Poem

🐰 A staging tag here, a release there,
Immutable tokens to mark what we care,
From patch-only bumps to major promotion,
Two workflows now sail with purpose and motion!
With cleanup on failure and scripts to resolve,
These releases shall flow, no chaos to devolve.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: splitting staging and release workflows and enabling production releases from staging tags.
Linked Issues check ✅ Passed The PR implements all core objectives from #1089: separate staging/release workflows, staging patch-only tags, production source selection (staging tag default or main), helper scripts, and documentation.
Out of Scope Changes check ✅ Passed All changes are within scope: new workflows, helper scripts, documentation updates, and workflow refactoring directly support the stated objectives.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Review rate limit: 3/5 reviews remaining, refill in 16 minutes and 38 seconds.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

162-181: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

build_ref still points at the new release tag, not the resolved source.

Lines 115-121 say the matrix should build the selected source, but Line 169 assigns BUILD_REF="$TAG". That makes every downstream checkout build the freshly bumped commit on main, so a release_source=staging_tag run can still ship code that staging never exercised.

Either create the release bump/tag on top of the chosen source commit, or keep build_ref bound to steps.source.outputs.ref/sha and inject the production version separately during packaging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 162 - 181, The workflow sets
BUILD_REF="$TAG" which causes downstream checkouts to build the new release tag
rather than the selected source commit; change the logic so BUILD_REF is the
resolved source (use steps.source.outputs.ref or steps.source.outputs.sha as the
build ref) or, if you must keep TAG as the release identifier, create the
bump/tag on top of that selected source commit before setting BUILD_REF; update
the Resolve build outputs step to export build_ref from the source step (e.g.,
steps.source.outputs.ref or .sha) and keep TAG only for packaging/release
metadata (symbols: BUILD_REF, TAG, steps.source.outputs.ref,
steps.source.outputs.sha).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Around line 11-18: Summary: The table entry for **`app/`** still calls it a
"Yarn workspace" while the repo uses pnpm; update the wording to avoid
contradicting the note about pnpm. Fix: edit the CLAUDE.md table row for
**`app/`** to replace "Yarn workspace `openhuman-app`" with "pnpm workspace
`openhuman-app`" (or "pnpm workspace / Vite + React...") and ensure the
parenthetical sentence about repo migration remains consistent; check the
surrounding sentence that mentions "migrated from yarn to pnpm" and harmonize
capitalization/terminology if present.

In `@scripts/release/resolve-release-source.sh`:
- Around line 39-51: When RELEASE_SOURCE is set to a staging tag the script
currently accepts any tag value in STAGING_TAG; add a guard to reject tags that
are not actual staging tags by validating STAGING_TAG matches the expected
staging prefix (e.g. /^staging\// or 'staging/v*') before accepting it. Update
scripts/release/resolve-release-source.sh to check STAGING_TAG (the variable
used to set REF and compute SHA) and if it does not match the staging pattern,
print an error and exit nonzero; leave the existing git rev-parse checks for
refs/tags/${STAGING_TAG} in place after this validation.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 162-181: The workflow sets BUILD_REF="$TAG" which causes
downstream checkouts to build the new release tag rather than the selected
source commit; change the logic so BUILD_REF is the resolved source (use
steps.source.outputs.ref or steps.source.outputs.sha as the build ref) or, if
you must keep TAG as the release identifier, create the bump/tag on top of that
selected source commit before setting BUILD_REF; update the Resolve build
outputs step to export build_ref from the source step (e.g.,
steps.source.outputs.ref or .sha) and keep TAG only for packaging/release
metadata (symbols: BUILD_REF, TAG, steps.source.outputs.ref,
steps.source.outputs.sha).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f651ed18-afec-4c23-9d22-1f358741dbb3

📥 Commits

Reviewing files that changed from the base of the PR and between 37cf31b and d7ae26b.

📒 Files selected for processing (6)
  • .github/workflows/release-staging.yml
  • .github/workflows/release.yml
  • CLAUDE.md
  • docs/RELEASE.md
  • scripts/release/next-staging-tag.js
  • scripts/release/resolve-release-source.sh

Comment thread CLAUDE.md
Comment on lines +11 to 18
| Path | Role |
| ----------------------- | -------------------------------------------------------------------------------------------------------------- |
| **`app/`** | Yarn workspace `openhuman-app`: Vite + React (`app/src/`), Tauri desktop host (`app/src-tauri/`), Vitest tests |
| **`src/`** (root) | Rust lib `openhuman_core` + `openhuman` CLI binary — `core_server`, `openhuman::*` domains, MCP routing |
| **`Cargo.toml`** (root) | Core crate; `cargo build --bin openhuman` produces the sidecar staged by `app`'s `core:stage` |
| **`docs/`** | Architecture and module guides |

Commands assume the **repo root**; `pnpm dev` delegates to the `app` workspace. (Repo migrated from yarn to pnpm — `package.json` enforces pnpm via the `packageManager` field.)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace the stale “Yarn workspace” label.

Line 13 still calls app/ a Yarn workspace even though Line 18 says the repo now enforces pnpm. That contradiction will send contributors to the wrong tooling terminology.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 11 - 18, Summary: The table entry for **`app/`**
still calls it a "Yarn workspace" while the repo uses pnpm; update the wording
to avoid contradicting the note about pnpm. Fix: edit the CLAUDE.md table row
for **`app/`** to replace "Yarn workspace `openhuman-app`" with "pnpm workspace
`openhuman-app`" (or "pnpm workspace / Vite + React...") and ensure the
parenthetical sentence about repo migration remains consistent; check the
surrounding sentence that mentions "migrated from yarn to pnpm" and harmonize
capitalization/terminology if present.

Comment on lines +39 to +51
if [ -z "$STAGING_TAG" ]; then
STAGING_TAG="$(git tag --list 'staging/v*' --sort=-creatordate | head -n 1)"
fi
if [ -z "$STAGING_TAG" ]; then
echo "[resolve-release-source] no staging tags found matching 'staging/v*' — push a staging cut first or rerun with release_source=main_head" >&2
exit 1
fi
if ! git rev-parse --verify --quiet "refs/tags/${STAGING_TAG}" >/dev/null; then
echo "[resolve-release-source] staging tag '${STAGING_TAG}' does not exist on this remote" >&2
exit 1
fi
REF="$STAGING_TAG"
SHA="$(git rev-parse "refs/tags/${STAGING_TAG}^{commit}")"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject non-staging tags when RELEASE_SOURCE=staging_tag.

This path currently accepts any existing tag because it only checks refs/tags/${STAGING_TAG}. A dispatch with staging_tag=v1.2.3 would pass here and build from a production tag, which defeats the staging-promotion contract.

Suggested guard
 else
   if [ -z "$STAGING_TAG" ]; then
     STAGING_TAG="$(git tag --list 'staging/v*' --sort=-creatordate | head -n 1)"
   fi
   if [ -z "$STAGING_TAG" ]; then
     echo "[resolve-release-source] no staging tags found matching 'staging/v*' — push a staging cut first or rerun with release_source=main_head" >&2
     exit 1
   fi
+  if ! printf '%s\n' "$STAGING_TAG" | grep -Eq '^staging/v[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$'; then
+    echo "[resolve-release-source] STAGING_TAG must match staging/vX.Y.Z-N, got '${STAGING_TAG}'" >&2
+    exit 1
+  fi
   if ! git rev-parse --verify --quiet "refs/tags/${STAGING_TAG}" >/dev/null; then
     echo "[resolve-release-source] staging tag '${STAGING_TAG}' does not exist on this remote" >&2
     exit 1
   fi
📝 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
if [ -z "$STAGING_TAG" ]; then
STAGING_TAG="$(git tag --list 'staging/v*' --sort=-creatordate | head -n 1)"
fi
if [ -z "$STAGING_TAG" ]; then
echo "[resolve-release-source] no staging tags found matching 'staging/v*' — push a staging cut first or rerun with release_source=main_head" >&2
exit 1
fi
if ! git rev-parse --verify --quiet "refs/tags/${STAGING_TAG}" >/dev/null; then
echo "[resolve-release-source] staging tag '${STAGING_TAG}' does not exist on this remote" >&2
exit 1
fi
REF="$STAGING_TAG"
SHA="$(git rev-parse "refs/tags/${STAGING_TAG}^{commit}")"
if [ -z "$STAGING_TAG" ]; then
STAGING_TAG="$(git tag --list 'staging/v*' --sort=-creatordate | head -n 1)"
fi
if [ -z "$STAGING_TAG" ]; then
echo "[resolve-release-source] no staging tags found matching 'staging/v*' — push a staging cut first or rerun with release_source=main_head" >&2
exit 1
fi
if ! printf '%s\n' "$STAGING_TAG" | grep -Eq '^staging/v[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$'; then
echo "[resolve-release-source] STAGING_TAG must match staging/vX.Y.Z-N, got '${STAGING_TAG}'" >&2
exit 1
fi
if ! git rev-parse --verify --quiet "refs/tags/${STAGING_TAG}" >/dev/null; then
echo "[resolve-release-source] staging tag '${STAGING_TAG}' does not exist on this remote" >&2
exit 1
fi
REF="$STAGING_TAG"
SHA="$(git rev-parse "refs/tags/${STAGING_TAG}^{commit}")"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/release/resolve-release-source.sh` around lines 39 - 51, When
RELEASE_SOURCE is set to a staging tag the script currently accepts any tag
value in STAGING_TAG; add a guard to reject tags that are not actual staging
tags by validating STAGING_TAG matches the expected staging prefix (e.g.
/^staging\// or 'staging/v*') before accepting it. Update
scripts/release/resolve-release-source.sh to check STAGING_TAG (the variable
used to set REF and compute SHA) and if it does not match the staging pattern,
print an error and exit nonzero; leave the existing git rev-parse checks for
refs/tags/${STAGING_TAG} in place after this validation.

@senamakel senamakel closed this May 2, 2026
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.

[Feature] Split staging vs release workflows; release from staging tag (default) or main; staging patch tags

1 participant