Skip to content

ci(release): split release into read-only gate + minimal publish job - #7

Merged
thenotoriousllama merged 1 commit into
mainfrom
feature/ci-workflows
Jul 2, 2026
Merged

ci(release): split release into read-only gate + minimal publish job#7
thenotoriousllama merged 1 commit into
mainfrom
feature/ci-workflows

Conversation

@thenotoriousllama

@thenotoriousllama thenotoriousllama commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Applies the same least-privilege release split that CodeRabbit required on hivenectar PR #11 to hivedoctor's release pipeline (this repo was explicitly opened for modify-as-needed upgrades).

  • The single release job, which ran npm ci, the full gate, the build, and all guards while holding contents: write + id-token: write, is now two jobs:
    • gate (contents: read): npm ci, npm run ci, pack-check, the telemetry-keyed build, tag/version guard, publishability preflight, publish-mode resolution, then uploads bundle/ as an artifact.
    • publish (contents: write + id-token: write): downloads the gate-built bundle/ and publishes it verbatim with --ignore-scripts, so prepack never reruns and no repository or third-party code executes under the OIDC publish identity.
  • Deliberate ordering fix inside the gate, documented in the file header: pack:check triggers a keyless prepack rebuild, so the telemetry-keyed build runs after it, guaranteeing the uploaded artifact carries the baked PostHog define.
  • All existing behavior preserved: fail-closed preflights, dry-run-by-default dispatch, tag-only real publishes, npm-view idempotency guard, OIDC diagnostics, SHA-pinned actions (upload/download-artifact pinned to v7.0.1/v8.0.1 SHAs).

Test plan

  • YAML validated with a parser locally; per-job permissions confirmed (gate: contents: read; publish: contents: write, id-token: write).
  • Rehearsable end-to-end without publishing: run the workflow via workflow_dispatch (dry_run defaults to true) after merge to prove the artifact handoff and the --ignore-scripts pack path before the next real tag.

Summary by CodeRabbit

  • Bug Fixes

    • Improved the release flow to prevent accidental rebuilds during publishing and ensure the packaged app is the one released.
    • Made real publishing happen only when a new version is needed, reducing duplicate or unnecessary releases.
  • Chores

    • Tightened release permissions and separated build and publish steps for a safer, more reliable deployment process.
    • Added artifact handoff between build and publish steps to keep release output consistent.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The release workflow is restructured into two jobs: a read-only gate job that installs dependencies, runs checks, pack:check, and builds a telemetry-keyed bundle, and a privileged publish job that downloads the built artifact and handles OIDC/Trusted Publishing npm publish and GitHub Release creation, gated by job outputs.

Changes

Release Workflow Restructuring

Layer / File(s) Summary
Pipeline overview docs
.github/workflows/release.yaml
Top-of-file comments updated to describe Trusted Publishing/provenance setup and the new gate/publish job split.
Read-only gate job: checks and build ordering
.github/workflows/release.yaml
New gate job (contents: read) runs checkout/setup-node without registry auth, npm ci/npm run ci, adds explicit pack:check step before the telemetry-keyed build, and exports a publish output.
Artifact handoff and privileged publish job
.github/workflows/release.yaml
Gate uploads bundle/ via upload-artifact; publish job downloads it, configures npm for Trusted Publishing (registry-url, npm upgrade, stripped _authToken), and gates real publish, dry-run rehearsal, and GitHub Release steps on needs.gate.outputs.publish, publishing with --ignore-scripts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Trigger as Workflow trigger
  participant Gate as gate job
  participant Publish as publish job
  participant NpmRegistry as npm registry
  participant GitHub as GitHub Release

  Trigger->>Gate: checkout, npm ci, npm run ci
  Gate->>Gate: pack:check (prepack)
  Gate->>Gate: build with telemetry env vars
  Gate->>Gate: upload bundle/ artifact
  Gate-->>Publish: outputs.publish (real/dry-run)
  Publish->>Publish: download bundle/ artifact
  Publish->>Publish: configure OIDC/Trusted Publishing
  alt outputs.publish == real and version not on npm
    Publish->>NpmRegistry: npm publish --provenance --ignore-scripts
    Publish->>GitHub: create Release
  else dry-run or already published
    Publish->>Publish: skip real publish/release
  end
Loading

Possibly related PRs

  • legioncodeinc/honeycomb#44: Both PRs modify the same .github/workflows/release.yaml to implement a guarded npm publish flow with a quality gate and pack:check before publishing.
  • legioncodeinc/honeycomb#105: Overlaps directly on the release.yaml refactor to Trusted Publishing/OIDC with tokenless publish-mode resolution and provenance flags.
  • legioncodeinc/honeycomb#173: Both PRs change how "real publish" completion is surfaced via job outputs to gate subsequent workflow steps.

Poem

A gate stands guard, then hops away,
A bundle built for publish day,
No tokens hide, just OIDC light,
The rabbit ships with provenance bright,
Thump-thump — release goes out just right! 🐇📦

🚥 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: splitting the release workflow into least-privilege gate and publish jobs.
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 feature/ci-workflows

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: 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/release.yaml:
- Around line 257-268: The release workflow’s OIDC auth cleanup is happening too
late, so the scaffolded `_authToken` from setup-node can still affect the first
npm command. Move the “Enable OIDC trusted publishing (strip scaffolded auth
token)” step to run before the “Upgrade npm” step in the release workflow,
keeping the auth-strip logic in place and leaving any --ignore-scripts hardening
optional.
🪄 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 Plus

Run ID: 06389f2a-95bd-4678-bd1c-2c76429120cc

📥 Commits

Reviewing files that changed from the base of the PR and between c6e6187 and f77c9d1.

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

Comment on lines +257 to +268
- name: Upgrade npm (Trusted Publishing needs npm >= 11.5.1)
run: npm install -g npm@11.6.2

# Strip setup-node's dummy `_authToken` line so OIDC authenticates the
# publish (npm uses OIDC only when NO credential is configured). The
# registry line is KEPT. Diagnostics printed (token redacted) for a future
# auth failure.
- name: Enable OIDC trusted publishing (strip scaffolded auth token)
run: |
npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}"
if [ -f "$npmrc" ]; then
sed -i '/:_authToken=/d' "$npmrc"

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

# Inspect the workflow context around the flagged lines.
nl -ba .github/workflows/release.yaml | sed -n '220,310p'

# Find setup-node/auth-token related steps in this workflow.
rg -n "setup-node|NODE_AUTH_TOKEN|_authToken|npm install -g npm@" .github/workflows/release.yaml

Repository: legioncodeinc/hivedoctor

Length of output: 202


🌐 Web query:

npm install -g ignore-scripts lifecycle scripts default behavior npm auth token setup-node OIDC trusted publishing

💡 Result:

As of July 2026, significant changes to npm's default security behavior regarding lifecycle scripts and authentication are in effect, particularly with the release of npm v12 [1][2]. Lifecycle Scripts and ignore-scripts Historically, npm ran lifecycle scripts (preinstall, install, postinstall) automatically by default, and the --ignore-scripts flag was the primary method to prevent this execution [3][4][1]. As of npm v12, lifecycle scripts are blocked by default [5][1][2]. Projects must now opt-in to running these scripts for specific dependencies by using an allowlist (managed via npm approve-scripts and the allowScripts field in package.json) [6][7][1][2]. When using --ignore-scripts (on older versions or as a legacy configuration): - It instructs npm not to execute lifecycle scripts during installation [3][4]. - It does not prevent explicit commands such as npm start, npm stop, or npm run-script from executing their respective scripts, though it may skip associated pre- or post-scripts [3][8]. Trusted Publishing (OIDC) and Authentication Trusted publishing allows publishing to npm from CI/CD environments (like GitHub Actions) using OpenID Connect (OIDC) instead of static long-lived tokens [9][10]. This requires npm version 11.5.1 or later [10]. Configuration Conflict: A common issue arises when using actions/setup-node with the registry-url configuration in environments intended for OIDC publishing [11][12]. If registry-url is set without an explicit NODE_AUTH_TOKEN, the action may write a placeholder _authToken entry to the.npmrc file [11][12]. When npm sees this entry—even if the token value is effectively empty—it may attempt traditional token-based authentication and fail to initiate the OIDC exchange, resulting in ENEEDAUTH or E404 errors [11][12]. To resolve this when using Trusted Publishing: - Avoid using the registry-url input in setup-node if it causes this conflict [12]. - Ensure your workflow has id-token: write permissions [10]. - Verify that your Trusted Publisher configuration on npm (matching the repository/workflow) is correctly set [9][10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/release.yaml')
lines = p.read_text().splitlines()
for i in range(220, 311):
    if i <= len(lines):
        print(f"{i:4d}: {lines[i-1]}")
PY

printf '\n-- matches --\n'
grep -nE 'setup-node|NODE_AUTH_TOKEN|_authToken|npm install -g npm@' .github/workflows/release.yaml || true

Repository: legioncodeinc/hivedoctor

Length of output: 6078


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read just the relevant workflow step order with a small parser.
python3 - <<'PY'
from pathlib import Path
import re
text = Path('.github/workflows/release.yaml').read_text().splitlines()
for idx, line in enumerate(text, 1):
    if any(k in line for k in ('setup-node', 'NODE_AUTH_TOKEN', '_authToken', 'npm install -g npm@', 'trusted publishing', 'enable oidc', 'Upgrade npm')):
        print(f"{idx}: {line}")
PY

Repository: legioncodeinc/hivedoctor

Length of output: 919


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/release.yaml')
for i, line in enumerate(p.read_text().splitlines(), 1):
    if 240 <= i <= 290:
        print(f"{i:4d}: {line}")
PY

Repository: legioncodeinc/hivedoctor

Length of output: 3155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for path in Path('.').rglob('*'):
    if path.is_file() and path.suffix in {'.yaml', '.yml', '.json', '.md', '.sh', '.js', '.ts'}:
        try:
            text = path.read_text()
        except Exception:
            continue
        if 'NODE_AUTH_TOKEN' in text or '_authToken' in text:
            print(path)
            for i, line in enumerate(text.splitlines(), 1):
                if 'NODE_AUTH_TOKEN' in line or '_authToken' in line:
                    print(f"  {i}: {line}")
PY

Repository: legioncodeinc/hivedoctor

Length of output: 520


🌐 Web query:

npm install -g lifecycle scripts install scripts --ignore-scripts documentation global install public package registry auth token setup-node dummy _authToken

💡 Result:

The npm configuration settings and practices you inquired about relate to managing security during installation and authentication in automated environments. Lifecycle Scripts and --ignore-scripts Lifecycle scripts (such as postinstall) execute arbitrary commands during the package installation process [1][2]. While these scripts are useful for build tasks like native compilation, they also represent a significant security risk, as malicious packages can use them to execute commands on your system [3][1]. The --ignore-scripts flag is used to prevent the execution of these lifecycle scripts during npm install commands [4][5]. It can be passed as a command-line argument: npm install --ignore-scripts To make this the default behavior for all installations, you can add it to your.npmrc file: ignore-scripts=true Please note that while --ignore-scripts mitigates many risks, it is not a complete security solution. Some vulnerabilities have been identified where specific npm processes may still bypass this flag [6]. Additionally, it will break packages that rely on scripts for necessary compilation [2]. Public Registry Authentication and setup-node When using GitHub Actions, the setup-node action is commonly used to configure authentication for the npm registry [7][8]. The action automates the creation of an.npmrc file containing the registry URL and an _authToken placeholder [9]. In traditional token-based authentication, you provide a secret (typically stored as NODE_AUTH_TOKEN) which is injected into the.npmrc file [7][8]. The configuration in.npmrc generally looks like this: //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} Dummy Token and OIDC The actions/setup-node action may automatically write a placeholder for the _authToken even if no specific secret is provided [9][10]. This behavior is designed to support traditional auth flows [10]. However, this can sometimes interfere with modern npm Trusted Publisher (OIDC) authentication flows, as the presence of an _authToken line might cause npm to skip the OIDC token exchange process [10]. If you are using OIDC-based publishing and encounter issues, you can work around the presence of the dummy token by unsetting or clearing the NODE_AUTH_TOKEN environment variable before running your publish command [11]: NODE_AUTH_TOKEN="" npm publish Using modern versions of Node.js (v24+) and npm (v11+) is recommended for native support of the OIDC trusted publishing flow [11].

Citations:


🌐 Web query:

actions/setup-node registry-url _authToken NODE_AUTH_TOKEN placeholder .npmrc npm install -g public package ENEEDAUTH

💡 Result:

The ENEEDAUTH error occurs because npm detects an _authToken configuration in your .npmrc file and assumes it must use that token for authentication, even if it is empty [1][2]. When you use the registry-url input in the actions/setup-node action, it automatically generates a temporary .npmrc file containing a placeholder line: //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} [1][2][3]. If you do not provide a valid NODE_AUTH_TOKEN secret in your environment, this placeholder expands to an empty string [2]. Because the file now explicitly contains an empty _authToken, the npm CLI incorrectly decides that authentication is "configured" and skips its automatic mechanisms (such as Trusted Publisher OIDC or standard interactive login), leading to the ENEEDAUTH or E404 error [1][2]. To resolve this, choose one of the following approaches: 1. Use Trusted Publishers (OIDC): If you are using GitHub's Trusted Publishers (which does not require a long-lived secret), you must prevent actions/setup-node from writing the conflicting auth line [1][2]. Currently, the most reliable way to achieve this is to avoid using the registry-url input in the setup-node step [2]. Instead, configure the registry manually or ensure your workflow is configured in a way that doesn't trigger the automatic .npmrc generation [2]. 2. Provide the Token: If you are using a classic token-based flow, ensure the NODE_AUTH_TOKEN environment variable is correctly set in your step's env block [4][5]: jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.YOUR_NPM_TOKEN }} 3. Manual Configuration: If you need custom registry behavior or are having trouble with the action's automatic file generation, you can manually configure the registry and auth without the registry-url input [2]. You can use npm config set commands or create a local .npmrc file directly within your workflow steps, ensuring it is properly populated with your secrets [4][6][7]. Regarding npm install -g: Be aware that global installations in CI environments often ignore user-level .npmrc files or have different registry resolution behaviors depending on the OS and npm version [7]. It is generally recommended to install packages locally in your project or use npx to execute global-style tools [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for path in Path('.').rglob('*'):
    if not path.is_file():
        continue
    try:
        text = path.read_text()
    except Exception:
        continue
    if 'NODE_AUTH_TOKEN' in text or '_authToken' in text:
        print(path)
        for i, line in enumerate(text.splitlines(), 1):
            if 'NODE_AUTH_TOKEN' in line or '_authToken' in line:
                print(f"  {i}: {line}")
PY

Repository: legioncodeinc/hivedoctor

Length of output: 520


🌐 Web query:

npm@11.6.2 package scripts lifecycle preinstall postinstall registry package metadata

💡 Result:

In npm version 11.6.2, lifecycle scripts and package metadata registry interactions function according to established npm standards [1][2]. Lifecycle scripts allow users to run arbitrary commands at specific stages of a package's installation or lifecycle [3][4]. The preinstall and postinstall scripts are defined within the scripts object of a package.json file [3][5]. When running npm install, these scripts are executed in the sequence: preinstall, install, and then postinstall [3][6]. These scripts are intended primarily for tasks like compilation that must occur on the target architecture [3][7]. Regarding registry interaction and metadata, the npm registry provides metadata documents for packages, which include information parsed from the package.json file, such as version, dependencies, and scripts [8][9]. When a package is published, the registry processes this metadata [8]. For efficient installation, the registry supports an abbreviated metadata format that includes an allow-list of fields; specifically, the abbreviated version object contains a boolean field, hasInstallScript, which is set to true if that version includes install scripts [8]. This allows the npm client to determine if lifecycle scripts need to be executed without downloading the full package metadata [8]. NPM version 11.6.2, released on October 8, 2025, focused primarily on bug fixes, code cleanup, and documentation updates rather than altering the core architecture of lifecycle scripts or registry metadata handling [2][10].

Citations:


Move the auth-strip step before the npm upgrade. The current order leaves setup-node’s scaffolded _authToken in place for the first registry-touching npm command, which can derail the release flow; --ignore-scripts is optional hardening, not the core fix.

🤖 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/release.yaml around lines 257 - 268, The release
workflow’s OIDC auth cleanup is happening too late, so the scaffolded
`_authToken` from setup-node can still affect the first npm command. Move the
“Enable OIDC trusted publishing (strip scaffolded auth token)” step to run
before the “Upgrade npm” step in the release workflow, keeping the auth-strip
logic in place and leaving any --ignore-scripts hardening optional.

@thenotoriousllama
thenotoriousllama merged commit c6101ca into main Jul 2, 2026
4 checks passed
@thenotoriousllama
thenotoriousllama deleted the feature/ci-workflows branch July 2, 2026 10:16
@thenotoriousllama
thenotoriousllama restored the feature/ci-workflows branch July 2, 2026 12:16
@thenotoriousllama
thenotoriousllama deleted the feature/ci-workflows branch July 2, 2026 22:24
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