Skip to content

fix(publish): authenticate to GitHub Packages before installing - #92

Merged
Ryan Zhu (underthestars-zhy) merged 1 commit into
mainfrom
fix/publish-authenticate-private-deps
Jul 31, 2026
Merged

fix(publish): authenticate to GitHub Packages before installing#92
Ryan Zhu (underthestars-zhy) merged 1 commit into
mainfrom
fix/publish-authenticate-private-deps

Conversation

@underthestars-zhy

@underthestars-zhy Ryan Zhu (underthestars-zhy) commented Jul 31, 2026

Copy link
Copy Markdown
Member

The problem

A published package whose own dependencies live on GitHub Packages cannot be released. Both publish blocks run bun install with no registry credentials:

warn: GET https://npm.pkg.github.com/@photon-hq%2fdb - 401
error: GET https://npm.pkg.github.com/@photon-hq/db/-/db-0.1.0.tgz - 403

connect-basic hit this on v0.10.0. The version was bumped and the GitHub Release created, but no artifact was ever published — a tagged release pointing at nothing.

This is latent for any published package with a first-party dependency. hono-ts uses this workflow and carries @photon-hq/error as a devDependency.

The fix

Write the token to ~/.npmrc before install — credentials only, no scope mapping.

The existing comment explains why the owner's scope must not be remapped this early:

This keeps ordinary npmjs.org dependencies resolvable when a package is being published to both registries for the first time.

That property is preserved. Supplying a token for the host is orthogonal to which scope routes there: a repository that genuinely routes a scope to GitHub Packages already declares it in its own .npmrc, and one that does not is unaffected because nothing resolves to that host in the first place.

Applied to both publish-github-package (single) and publish-github-packages (monorepo).

Not covered

publish-npm has the same shape but only accepts an npm-token, so giving it GitHub Packages credentials means adding an input. No package currently publishes to npmjs.org with a private first-party dependency, so I left it — happy to add it if you'd rather close the gap now.

Note

connect-basic is separately dropping the dependency that surfaced this (connect-ts#8) because a chassis package shouldn't couple its release to a sibling's registry. That unblocks it independently of this PR; this one is the general fix.

🤖 Generated with Claude Code


Note

Low Risk
CI-only composite action ordering change; no runtime app code, and it preserves the existing deferred scope-mapping behavior.

Overview
Adds a pre-install GitHub Packages auth step to the publish-github-package and publish-github-packages composite actions so bun install can fetch first-party dependencies from npm.pkg.github.com without failing with 401/403.

The new step appends only //npm.pkg.github.com/:_authToken=… to ~/.npmrc using the existing github-token input. It deliberately does not remap the repository owner’s npm scope to GitHub Packages—that mapping still happens later via actions/setup-node, preserving the behavior where early scope remapping would break dual-registry first-time publishes.

Comments in both action files were tightened to explain why credentials precede install while scope routing stays deferred.

Reviewed by Cursor Bugbot for commit f321435. Bugbot is set up for automated code reviews on this repo. Configure here.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Bug Fixes
    • Improved npm authentication for package publishing workflows by ensuring the GitHub Packages token is configured before dependencies are installed.
    • Prevented premature package scope mapping during setup.

A package whose own dependencies live on GitHub Packages could not be
released. Both publish blocks ran `bun install` with no registry credentials,
so a private first-party dependency failed with 401 then 403 and the publish
step never ran. connect-basic hit this on v0.10.0: the version was bumped and
the GitHub Release created, but no artifact was ever published.

The token is now written to ~/.npmrc before install. Deliberately credentials
only — no scope mapping. The existing ordering comment explains why the
owner's scope must not be remapped this early: a package being published to
both registries for the first time would stop resolving its own npmjs.org
scope-siblings. Supplying a token for the host without touching the registry
mapping fixes the install and preserves that property, because a repository
that genuinely routes a scope to GitHub Packages already declares it in its
own .npmrc.

This is latent for any published package with a first-party dependency, not
specific to connect-basic — hono-ts uses this workflow and carries
@photon-hq/error as a devDependency.

publish-npm has the same shape and is not covered here: it only accepts an
npm-token, so giving it GitHub Packages credentials means a new input, and no
package currently publishes to npmjs.org with a private first-party dependency.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 31, 2026 21:30

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Both GitHub Packages publishing actions now write the provided authentication token to $HOME/.npmrc before dependency installation. Scope mapping remains separate and is not configured by these changes.

Changes

GitHub Packages authentication

Layer / File(s) Summary
Add npm credential setup
.github/blocks/publish-github-package/action.yaml, .github/blocks/publish-github-packages/action.yaml
Both composite actions append inputs.github-token to $HOME/.npmrc before installation. The installation comment states that scope mapping is deferred.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: yanxue06

Poem

A rabbit writes a token with care,
Into npm’s config waiting there.
Scope paths stay apart,
Credentials start,
And packages hop through the air.

🚥 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 describes authenticating to GitHub Packages before dependency installation, which is the main change.
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 fix/publish-authenticate-private-deps

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

@github-actions

Copy link
Copy Markdown

📚 Skills documentation may need an update

This PR introduces changes that might not be reflected in the skills documentation.

Reason: The PR adds two new public BuildSpace composite blocks for publishing single-package and monorepo packages to GitHub Packages, but _skills-repo/skills/buildspace-ci-cd/SKILL.md does not document this new publishing target, its required github-token/packages: write setup, or the new block inputs and behavior.

This is an automated check powered by AI. If the skills are intentionally unchanged, feel free to ignore this.

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

🤖 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/blocks/publish-github-package/action.yaml:
- Around line 51-57: The GitHub Packages authentication steps in
.github/blocks/publish-github-package/action.yaml lines 51-57 and
.github/blocks/publish-github-packages/action.yaml lines 49-55 must stop writing
tokens to $HOME/.npmrc. Use a temporary NPM_CONFIG_USERCONFIG file for each bun
install, export it for the install, and ensure the temporary file is deleted on
normal completion and failure via cleanup handling.
- Around line 44-57: The GitHub Packages authentication steps in
.github/blocks/publish-github-package/action.yaml lines 44-57 and
.github/blocks/publish-github-packages/action.yaml lines 45-55 must configure
the GitHub Packages registry before bun install for every install root. Add or
require the appropriate per-scope registry mapping while retaining npmjs.org
resolution for dual-registry packages; preserve the existing token
authentication 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2756ee6-64f4-4b7e-8aef-801fe641321e

📥 Commits

Reviewing files that changed from the base of the PR and between 43f12a6 and f321435.

📒 Files selected for processing (2)
  • .github/blocks/publish-github-package/action.yaml
  • .github/blocks/publish-github-packages/action.yaml
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: check-skills / check-skills
  • GitHub Check: check-readme / check-readme

Comment on lines +44 to +57
# Credentials only, no scope mapping. A package whose own dependencies
# live on GitHub Packages needs a token to install them, but remapping the
# owner's scope here would break a package being published to both
# registries for the first time — its npmjs.org scope-siblings would stop
# resolving. Supplying the token without touching the registry mapping
# fixes the former and preserves the latter; a repository that routes a
# scope to GitHub Packages does so in its own .npmrc.
- name: Authenticate to GitHub Packages
shell: bash
env:
GITHUB_PACKAGES_TOKEN: ${{ inputs.github-token }}
run: |
printf '//npm.pkg.github.com/:_authToken=%s\n' \
"$GITHUB_PACKAGES_TOKEN" >> "$HOME/.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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 'publish-github-packages?' .github/workflows || true

mapfile -t npmrc_files < <(fd -H -t f '^\.npmrc$' .)
if ((${`#npmrc_files`[@]} == 0)); then
  echo "No .npmrc files found; inspect lockfiles or explicit registry URLs." >&2
  exit 1
fi

if ! rg -n -C 2 'npm\.pkg\.github\.com|:registry=' "${npmrc_files[@]}"; then
  echo "No GitHub Packages registry mapping was found." >&2
  exit 1
fi

Repository: photon-hq/buildspace

Length of output: 8844


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- action definitions ---'
for f in .github/blocks/publish-github-package/action.yaml \
         .github/blocks/publish-github-packages/action.yaml; do
  if [ -f "$f" ]; then
    echo "### $f"
    cat -n "$f"
  fi
done

printf '%s\n' '--- workflow call sites and relevant inputs ---'
rg -n -C 12 'uses: .*publish-github-packages?|working-directory:|root-build-command:|build-command:' .github/workflows

printf '%s\n' '--- package and registry configuration files ---'
fd -H -t f '(^\.npmrc$|^bun\.fig$|^bunfig\.toml$|^package\.json$|^bun\.lockb$|^bun\.lock$|^npm-shrinkwrap\.json$|^package-lock\.json$|^yarn\.lock$|^pnpm-lock\.yaml$)' . \
  --exclude node_modules --exclude dist --exclude build | sort

printf '%s\n' '--- registry references ---'
rg -n -C 3 'npm\.pkg\.github\.com|:registry=|registry' . \
  -g '!node_modules/**' -g '!dist/**' -g '!build/**' \
  -g '.npmrc' -g 'bunfig.toml' -g 'bunfig.*' -g 'package.json' \
  -g '*.lock' -g '*.lockb' -g '*.yaml' -g '*.yml' || true

Repository: photon-hq/buildspace

Length of output: 32993


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- tracked package and registry files ---'
git ls-files | grep -E '(^|/)(\.npmrc|bunfig\.toml|package\.json|bun\.lockb?|package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock)$' || true

printf '%s\n' '--- all filesystem package and registry files ---'
find . -type f \( \
  -name .npmrc -o -name bunfig.toml -o -name 'package.json' \
  -o -name 'bun.lock' -o -name 'bun.lockb' -o -name package-lock.json \
  -o -name npm-shrinkwrap.json -o -name pnpm-lock.yaml -o -name yarn.lock \
\) -not -path './.git/*' -print

printf '%s\n' '--- all GitHub Packages references, including hidden paths ---'
rg -uuu -n -C 3 'npm\.pkg\.github\.com|:registry=|registry' . \
  -g '!./.git/**' | head -n 300 || true

printf '%s\n' '--- install commands and their working-directory context ---'
rg -uuu -n -C 5 'bun install|npm install|yarn install|pnpm install|working-directory' \
  .github . -g '!./.git/**' | head -n 300 || true

Repository: photon-hq/buildspace

Length of output: 48104


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
import re

actions = [
    Path(".github/blocks/publish-github-package/action.yaml"),
    Path(".github/blocks/publish-github-packages/action.yaml"),
]

config_names = {
    ".npmrc", "bunfig.toml", "package.json", "bun.lock", "bun.lockb",
    "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock",
}
configs = [
    p for p in Path(".").rglob("*")
    if p.is_file() and ".git" not in p.parts and p.name in config_names
]

for action in actions:
    text = action.read_text()
    install = text.index("run: bun install")
    setup = text.index("uses: actions/setup-node@v4")
    auth = text.index("Authenticate to GitHub Packages")
    assert auth < install < setup, action
    if "publish-github-package/action.yaml" in str(action):
        assert "working-directory: ${{ inputs.working-directory }}" in text[install - 300:install + 100]
        root = "inputs.working-directory"
    else:
        assert "run: bun install" in text
        root = "repository root"
    print(f"{action}: auth < install < setup-node; install root = {root}")

print("Registry/config files in this repository:")
for path in configs:
    print(path)
if not configs:
    print("(none)")
PY

Repository: photon-hq/buildspace

Length of output: 439


Ensure each install root selects GitHub Packages.

Both actions write only the token. bun install runs before actions/setup-node creates the registry mapping. If the single-package inputs.working-directory or monorepo root lacks an existing .npmrc or equivalent mapping, private dependencies resolve against npmjs.org and fail. Add or require the mapping before installation while preserving dual-registry support.

📍 Affects 2 files
  • .github/blocks/publish-github-package/action.yaml#L44-L57 (this comment)
  • .github/blocks/publish-github-packages/action.yaml#L45-L55
🤖 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/blocks/publish-github-package/action.yaml around lines 44 - 57, The
GitHub Packages authentication steps in
.github/blocks/publish-github-package/action.yaml lines 44-57 and
.github/blocks/publish-github-packages/action.yaml lines 45-55 must configure
the GitHub Packages registry before bun install for every install root. Add or
require the appropriate per-scope registry mapping while retaining npmjs.org
resolution for dual-registry packages; preserve the existing token
authentication behavior.

Comment on lines +51 to +57
- name: Authenticate to GitHub Packages
shell: bash
env:
GITHUB_PACKAGES_TOKEN: ${{ inputs.github-token }}
run: |
printf '//npm.pkg.github.com/:_authToken=%s\n' \
"$GITHUB_PACKAGES_TOKEN" >> "$HOME/.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

printf '%s\n' '--- candidate files ---'
git ls-files '.github/blocks/*publish-github*/*'

printf '%s\n' '--- package action ---'
sed -n '1,120p' .github/blocks/publish-github-package/action.yaml

printf '%s\n' '--- packages action ---'
sed -n '1,120p' .github/blocks/publish-github-packages/action.yaml

printf '%s\n' '--- related npm configuration and install usage ---'
rg -n -C 4 'npmrc|NPM_CONFIG_USERCONFIG|bun install|npm install|yarn install|pnpm install|npm\.pkg\.github\.com' .github/blocks .github/workflows 2>/dev/null || true

Repository: photon-hq/buildspace

Length of output: 20654


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

files = [
    Path(".github/blocks/publish-github-package/action.yaml"),
    Path(".github/blocks/publish-github-packages/action.yaml"),
]

for path in files:
    lines = path.read_text().splitlines()
    auth = next(
        i for i, line in enumerate(lines)
        if 'printf \'//npm.pkg.github.com/:_authToken=%s\\n\'' in line
    )
    install = next(
        i for i, line in enumerate(lines)
        if re.search(r'\brun:\s*bun install\s*$', line)
    )
    cleanup = [
        i + 1 for i, line in enumerate(lines)
        if any(token in line for token in ('rm -f', 'unset NPM_CONFIG_USERCONFIG', 'NPM_CONFIG_USERCONFIG='))
    ]
    home_write = any(
        '$HOME/.npmrc' in line and ('>>' in line or '>' in line)
        for line in lines
    )
    print(path)
    print(f"  token_write_line={auth + 1}")
    print(f"  install_line={install + 1}")
    print(f"  token_write_precedes_install={auth < install}")
    print(f"  writes_user_npmrc={home_write}")
    print(f"  cleanup_or_temp_config_lines={cleanup}")
    print(f"  later_steps_after_install={len(lines[install + 1:]) > 0}")
PY

printf '%s\n' '--- action invocations ---'
rg -n -C 3 'publish-github-package(s)?/action\.yaml|publish-github-package(s)?@' .github . 2>/dev/null \
  -g '!node_modules' -g '!dist' -g '!build' | head -200 || true

Repository: photon-hq/buildspace

Length of output: 3611


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package action post-install section ---'
sed -n '100,130p' .github/blocks/publish-github-package/action.yaml

printf '%s\n' '--- monorepo action post-install section ---'
sed -n '56,90p' .github/blocks/publish-github-packages/action.yaml

printf '%s\n' '--- workflow context after composite actions ---'
sed -n '215,275p' .github/workflows/typescript-monorepo-release.yaml
sed -n '235,285p' .github/workflows/typescript-service-release.yaml

Repository: photon-hq/buildspace

Length of output: 5510


Do not persist GitHub Packages credentials in $HOME/.npmrc.

Both actions append the token before bun install and leave it in the user-wide npm configuration. Use a temporary NPM_CONFIG_USERCONFIG file for each install and delete it on exit, including failure paths.

📍 Affects 2 files
  • .github/blocks/publish-github-package/action.yaml#L51-L57 (this comment)
  • .github/blocks/publish-github-packages/action.yaml#L49-L55
🤖 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/blocks/publish-github-package/action.yaml around lines 51 - 57, The
GitHub Packages authentication steps in
.github/blocks/publish-github-package/action.yaml lines 51-57 and
.github/blocks/publish-github-packages/action.yaml lines 49-55 must stop writing
tokens to $HOME/.npmrc. Use a temporary NPM_CONFIG_USERCONFIG file for each bun
install, export it for the install, and ensure the temporary file is deleted on
normal completion and failure via cleanup handling.

@underthestars-zhy
Ryan Zhu (underthestars-zhy) merged commit 7a20ca8 into main Jul 31, 2026
5 checks passed
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.

2 participants