Skip to content

Sign releases with SSH keys and document retention setting#12

Merged
tyl3r-ch merged 6 commits intomainfrom
v618
Apr 13, 2026
Merged

Sign releases with SSH keys and document retention setting#12
tyl3r-ch merged 6 commits intomainfrom
v618

Conversation

@tyl3r-ch
Copy link
Copy Markdown
Contributor

@tyl3r-ch tyl3r-ch commented Apr 13, 2026

Summary by CodeRabbit

  • Documentation
    • Added README guidance to retain Claude Code history beyond the default cleanup period.
    • Updated CHANGELOG to publish v6.1.8 and date v6.1.7.
    • Expanded release docs describing 1Password-backed SSH signing, creation and local verification of signed release commits and signed tags, and the signed “on-behalf-of” commit trailer.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@tyl3r-ch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 31 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 31 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29c77e60-46a6-45fa-affc-8e95b0526157

📥 Commits

Reviewing files that changed from the base of the PR and between d43756e and 9416335.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • RELEASING.md
📝 Walkthrough

Walkthrough

The workflow now loads an SSH signing identity from 1Password, writes signing keys to the runner, configures Git for SSH commit/tag signing, verifies the existing release commit on origin/main, creates a signed release commit (with an on-behalf-of trailer) and a signed tag, verifies signatures, and updates docs and changelog accordingly.

Changes

Cohort / File(s) Summary
Release Workflow
.github/workflows/release.yml
Add steps to locate existing release commit on origin/main, extract package.json version from that commit, store EXISTING_RELEASE_COMMIT, validate main hasn't advanced, load SSH signing secrets from 1Password, write keys to ${RUNNER_TEMP}/release-signing/, configure git for SSH signing (gpg.format=ssh, enable commit/tag signing, set gpg.ssh.allowedSignersFile), create signed commits (git commit -S with on-behalf-of trailer) and signed tags (git tag -s -m), verify signatures (git log --show-signature, git tag -v), and remove the static github-actions[bot] identity step.
Release Documentation
RELEASING.md
Document new 1Password-backed SSH signing prerequisites (OP_SERVICE_ACCOUNT_TOKEN_PUBLIC, OP_SSH_BASE_URL and formatting constraints), describe pre-signing validation, require signed commits/tags and the roast.codes verification for the on-behalf-of trailer, and update failure/abort behavior if main advanced.
Changelog & README
CHANGELOG.md, README.md
Bump release to 6.1.8; add entries describing the 1Password + SSH signing release flow and maintainer/verified-tag prerequisites; add README section advising users to increase cleanupPeriodDays in ~/.claude/settings.json to retain Claude Code history.

Sequence Diagram(s)

sequenceDiagram
  participant Runner as Runner (GitHub Actions)
  participant OP as 1Password
  participant GitLocal as Git (local repo)
  participant Remote as GitHub (origin/main)

  Runner->>GitLocal: fetch origin/main and search for release commit by message
  GitLocal-->>Runner: return matching commit SHA and package.json version
  Runner->>Runner: set EXISTING_RELEASE_COMMIT, abort if main advanced
  Runner->>OP: request SSH signing identity + allowed signers file
  OP-->>Runner: return SSH keys + signer metadata
  Runner->>GitLocal: write keys to ${RUNNER_TEMP}/release-signing/
  Runner->>GitLocal: configure gpg.format=ssh, enable commit/tag signing, set allowed signers
  Runner->>GitLocal: create signed commit (git commit -S) with on-behalf-of trailer
  GitLocal-->>Runner: show signature (git log --show-signature)
  Runner->>GitLocal: create signed tag (git tag -s) and verify (git tag -v)
  Runner->>Remote: push signed commit and signed tag
  Remote-->>Runner: accept push (remote may verify signatures)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibbled secrets from OP in a dash,

tucked keys in the runner, no stash left to trash,
signed the commit with an on-behalf-of cheer,
kissed the tag, then pushed it far and near,
hopped off, tail twitching — release is clear.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 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 two main changes: implementing SSH signing for releases and documenting a retention setting for Claude Code history.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 v618

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

Copy link
Copy Markdown

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

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)

76-80: ⚠️ Potential issue | 🔴 Critical

Retry mode can publish the wrong commit.

Lines 76-80 treat any matching package.json version as a safe resume, but Lines 200-216 still tag and push the current main HEAD. If a previous run failed after the version bump was pushed and new commits land on main before the rerun, this job will sign, tag, and publish the newer tree as the old version.

🔧 Suggested guard
          if [[ "${RELEASE_VERSION}" = "${CURRENT_VERSION}" ]]; then
+           RELEASE_COMMIT_SHA="$(git log origin/main --format=%H --grep "^v${RELEASE_VERSION}: Release$" -n 1)"
+           if [[ -z "${RELEASE_COMMIT_SHA}" ]]; then
+             echo "Could not locate the original release commit for ${RELEASE_VERSION}."
+             exit 1
+           fi
+           if [[ "$(git rev-parse HEAD)" != "${RELEASE_COMMIT_SHA}" ]]; then
+             echo "main has moved past ${RELEASE_VERSION}; cut a new release instead of retrying."
+             exit 1
+           fi
            echo "SHOULD_BUMP=false" >> "$GITHUB_ENV"
            echo "Release version already present on main. Continuing in retry mode."

Also applies to: 200-216

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

In @.github/workflows/release.yml around lines 76 - 80, The workflow treats a
matching RELEASE_VERSION and CURRENT_VERSION as safe to skip bumping
(SHOULD_BUMP=false) but later still tags/pushes current main HEAD (the tag/push
block around lines 200-216), which can sign and publish newer commits; to fix,
when detecting RELEASE_VERSION == CURRENT_VERSION capture and persist the commit
SHA where package.json has that version (e.g., git log -n1 --pretty=%H --
package.json) into a env var (e.g., EXISTING_RELEASE_COMMIT), then before
tagging/pushing verify that git rev-parse HEAD equals EXISTING_RELEASE_COMMIT
(or checkout EXISTING_RELEASE_COMMIT and create the tag from that SHA) and abort
if they differ; update the tag/push logic (the block that creates and pushes the
release tag) to use EXISTING_RELEASE_COMMIT for creating the tag/push when
SHOULD_BUMP=false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 3-12: The changelog currently has two "Unreleased" headings (the
header "## [6.1.8] - Unreleased" and an earlier "6.1.7" entry marked
unreleased), which is confusing; pick a single unreleased section and update the
other heading to a concrete released version or remove the "Unreleased" suffix.
Specifically, ensure only "## [6.1.8] - Unreleased" (or whichever version should
be unreleased) contains "Unreleased" and edit the "6.1.7" heading to a released
format (e.g., remove "Unreleased" or add the release date/version) so there is
only one Unreleased section in CHANGELOG.md.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 76-80: The workflow treats a matching RELEASE_VERSION and
CURRENT_VERSION as safe to skip bumping (SHOULD_BUMP=false) but later still
tags/pushes current main HEAD (the tag/push block around lines 200-216), which
can sign and publish newer commits; to fix, when detecting RELEASE_VERSION ==
CURRENT_VERSION capture and persist the commit SHA where package.json has that
version (e.g., git log -n1 --pretty=%H -- package.json) into a env var (e.g.,
EXISTING_RELEASE_COMMIT), then before tagging/pushing verify that git rev-parse
HEAD equals EXISTING_RELEASE_COMMIT (or checkout EXISTING_RELEASE_COMMIT and
create the tag from that SHA) and abort if they differ; update the tag/push
logic (the block that creates and pushes the release tag) to use
EXISTING_RELEASE_COMMIT for creating the tag/push when SHOULD_BUMP=false.
🪄 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: c1500121-e0ae-4b06-b72c-a1475cd9c12c

📥 Commits

Reviewing files that changed from the base of the PR and between 00d1a87 and ee296b0.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • CHANGELOG.md
  • README.md
  • RELEASING.md

Comment thread CHANGELOG.md Outdated
Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

163-171: Consider validating OP_SSH_BASE_URL format.

The secret references are constructed by concatenating OP_SSH_BASE_URL with field names (e.g., name, public key). If OP_SSH_BASE_URL doesn't end with a /, the references would be malformed (e.g., op://vault/itemname instead of op://vault/item/name).

The documentation correctly states the format should be op://vault/item/, but a missing trailing slash would cause cryptic 1Password errors at runtime.

💡 Optional: Add a validation step
      - name: Validate 1Password base URL format
        run: |
          if [[ ! "${{ secrets.OP_SSH_BASE_URL }}" =~ /$ ]]; then
            echo "OP_SSH_BASE_URL must end with a trailing slash"
            exit 1
          fi

Alternatively, this could be handled by normalizing the URL in the workflow itself.

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

In @.github/workflows/release.yml around lines 163 - 171, The 1Password secret
keys are built by concatenating OP_SSH_BASE_URL with field names inside the
load-release-secrets step using 1Password/load-secrets-action, so if
OP_SSH_BASE_URL lacks a trailing slash the constructed references (e.g.,
RELEASE_SIGNER_NAME, RELEASE_SIGNING_PUBLIC_KEY) will be malformed; add a
validation or normalization step before load-release-secrets that checks
OP_SSH_BASE_URL ends with a '/' (or appends one) and fails early with a clear
message if not, ensuring the secrets like RELEASE_SIGNER_NAME and
RELEASE_SIGNING_PUBLIC_KEY are built from a correctly-formed OP_SSH_BASE_URL.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 163-171: The 1Password secret keys are built by concatenating
OP_SSH_BASE_URL with field names inside the load-release-secrets step using
1Password/load-secrets-action, so if OP_SSH_BASE_URL lacks a trailing slash the
constructed references (e.g., RELEASE_SIGNER_NAME, RELEASE_SIGNING_PUBLIC_KEY)
will be malformed; add a validation or normalization step before
load-release-secrets that checks OP_SSH_BASE_URL ends with a '/' (or appends
one) and fails early with a clear message if not, ensuring the secrets like
RELEASE_SIGNER_NAME and RELEASE_SIGNING_PUBLIC_KEY are built from a
correctly-formed OP_SSH_BASE_URL.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9848cd61-6471-4bd9-9c60-e662d6107f36

📥 Commits

Reviewing files that changed from the base of the PR and between 03c8477 and 77364fd.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • RELEASING.md

Copy link
Copy Markdown

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

Caution

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

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

108-119: ⚠️ Potential issue | 🟠 Major

Retry mode still gates on a CI run for the release commit.

Line 112 always resolves HEAD, so once Lines 76-96 switch into retry mode the job asks verify-main-ci.js for a ci.yml run on the existing vX.Y.Z: Release commit. That makes the advertised resume path fail whenever the workflow-created version-bump push did not trigger CI.

Suggested fix
       - name: Verify main CI succeeded
+        if: env.SHOULD_BUMP == 'true'
         env:
           GITHUB_TOKEN: ${{ github.token }}
         run: |
           MAIN_SHA="$(git rev-parse HEAD)"
           node scripts/verify-main-ci.js \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 108 - 119, The job currently sets
MAIN_SHA via git rev-parse HEAD so verify-main-ci.js checks CI for the workflow
runner's HEAD, which causes failures when the workflow-created version-bump push
is the target; change the job to pass the actual release push commit SHA (use
the event payload SHA such as github.event.after or github.sha provided by
Actions) into MAIN_SHA before calling verify-main-ci.js so verify-main-ci.js is
asked about the release commit (MAIN_SHA) rather than local HEAD; update the
environment/variable assignment that defines MAIN_SHA and keep the
verify-main-ci.js invocation, workflow name (ci.yml) and branch main unchanged.

227-244: ⚠️ Potential issue | 🟠 Major

Verify pre-existing tags before treating a retry as resumable.

This code only verifies signatures for tags created in the current run. If vX.Y.Z already exists locally or on origin, the workflow skips tag creation and validation. The gh release create --verify-tag flag only confirms the tag exists in the remote repository—it does not validate the tag's signature or whether it points to the current HEAD. A stale or unsigned tag can therefore back the resumed release. (cli.github.com)

Add explicit signature and commit verification for pre-existing tags:

Suggested fix
           if git rev-parse "${RELEASE_TAG}" >/dev/null 2>&1; then
-            echo "Tag ${RELEASE_TAG} already exists locally. Skipping tag creation."
+            git tag -v "${RELEASE_TAG}"
+            if [[ "$(git rev-list -n 1 "${RELEASE_TAG}")" != "$(git rev-parse HEAD)" ]]; then
+              echo "Tag ${RELEASE_TAG} does not point at HEAD."
+              exit 1
+            fi
+            echo "Tag ${RELEASE_TAG} already exists locally and matches HEAD."
           else
             git tag -s "${RELEASE_TAG}" -m "${RELEASE_TAG}"
             git tag -v "${RELEASE_TAG}"
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 227 - 244, The workflow currently
skips tag creation/validation when ${RELEASE_TAG} already exists locally or on
origin; update the logic so that whenever a tag already exists (either branch
where git rev-parse or git ls-remote detects it) you explicitly verify the tag
signature and that the tag points to the intended commit: fetch the tag from
origin (git fetch --tags origin ${RELEASE_TAG}) if needed, run git tag -v
${RELEASE_TAG} (or git verify-tag) to ensure a valid signature, and compare the
tag target commit (git rev-list -n1 ${RELEASE_TAG} or git rev-parse
${RELEASE_TAG}^{object}) against HEAD (git rev-parse HEAD) to ensure it points
to the current release commit; if verification fails, exit non‑zero so the retry
is not treated as resumable. Ensure these checks are added in both places where
the script currently echoes "already exists" (the local existence branch around
git rev-parse and the remote existence branch after git ls-remote).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 108-119: The job currently sets MAIN_SHA via git rev-parse HEAD so
verify-main-ci.js checks CI for the workflow runner's HEAD, which causes
failures when the workflow-created version-bump push is the target; change the
job to pass the actual release push commit SHA (use the event payload SHA such
as github.event.after or github.sha provided by Actions) into MAIN_SHA before
calling verify-main-ci.js so verify-main-ci.js is asked about the release commit
(MAIN_SHA) rather than local HEAD; update the environment/variable assignment
that defines MAIN_SHA and keep the verify-main-ci.js invocation, workflow name
(ci.yml) and branch main unchanged.
- Around line 227-244: The workflow currently skips tag creation/validation when
${RELEASE_TAG} already exists locally or on origin; update the logic so that
whenever a tag already exists (either branch where git rev-parse or git
ls-remote detects it) you explicitly verify the tag signature and that the tag
points to the intended commit: fetch the tag from origin (git fetch --tags
origin ${RELEASE_TAG}) if needed, run git tag -v ${RELEASE_TAG} (or git
verify-tag) to ensure a valid signature, and compare the tag target commit (git
rev-list -n1 ${RELEASE_TAG} or git rev-parse ${RELEASE_TAG}^{object}) against
HEAD (git rev-parse HEAD) to ensure it points to the current release commit; if
verification fails, exit non‑zero so the retry is not treated as resumable.
Ensure these checks are added in both places where the script currently echoes
"already exists" (the local existence branch around git rev-parse and the remote
existence branch after git ls-remote).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22946603-6631-4070-a4a6-d2fc3f788e7b

📥 Commits

Reviewing files that changed from the base of the PR and between 77364fd and d43756e.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • RELEASING.md

@tyl3r-ch tyl3r-ch merged commit 3b30659 into main Apr 13, 2026
6 checks passed
@tyl3r-ch tyl3r-ch deleted the v618 branch April 13, 2026 23:13
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