Skip to content

Publish v0.2.36 to npm #1

Publish v0.2.36 to npm

Publish v0.2.36 to npm #1

Workflow file for this run

name: Publish npm
run-name: Publish ${{ inputs.tag }} to npm
on:
workflow_dispatch:
inputs:
tag:
description: Existing attested GitHub release tag, for example v0.2.14
required: true
type: string
confirm_publish:
description: Publish the verified release archive to npm
required: true
default: false
type: boolean
permissions: {}
concurrency:
group: npm-publish
cancel-in-progress: false
jobs:
verify:
name: Verify GitHub release
if: ${{ inputs.confirm_publish && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
outputs:
archive_name: ${{ steps.package.outputs.archive_name }}
archive_sha256: ${{ steps.package.outputs.archive_sha256 }}
artifact_id: ${{ steps.handoff.outputs.artifact-id }}
package_version: ${{ steps.package.outputs.package_version }}
env:
RELEASE_TAG: ${{ inputs.tag }}
steps:
- name: Check out the selected tag
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- name: Activate pinned release tools
run: |
corepack enable
corepack prepare pnpm@10.28.2 --activate
npm install --global --ignore-scripts --no-audit --no-fund npm@12.0.1
test "$(npm --version)" = "12.0.1"
- name: Require an exact release tag
run: |
node - <<'NODE'
const pkg = require('./packages/cli/package.json')
if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(pkg.version)) {
throw new Error(`package version ${pkg.version} is not a stable canonical semver`)
}
const expected = `v${pkg.version}`
if (process.env.RELEASE_TAG !== expected) {
throw new Error(`requested tag ${process.env.RELEASE_TAG} does not match ${expected}`)
}
NODE
TAG_COMMIT="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")"
test "$(git rev-parse HEAD)" = "$TAG_COMMIT"
- name: Download the existing GitHub release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
mkdir release
gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --dir release
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
--json assets,isDraft,isImmutable,isPrerelease,tagName > release.json
node - <<'NODE'
const release = require('./release.json')
const version = require('./packages/cli/package.json').version
if (release.isDraft || release.isPrerelease || !release.isImmutable || release.tagName !== process.env.RELEASE_TAG) {
throw new Error(`release ${process.env.RELEASE_TAG} is not the exact immutable public release`)
}
const { assets } = release
const actual = assets.map((asset) => asset.name).sort()
const expected = [
`codetruss-cli-${version}.sbom.cdx.json`,
`codetruss-cli-${version}.tgz`,
`codetruss-cli-${version}.tgz.sha256`,
].sort()
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new Error(`release assets differ from the exact publish set: ${actual.join(', ')}`)
}
NODE
rm release.json
- name: Verify release provenance
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="$(node -p "require('./packages/cli/package.json').version")"
TAG_COMMIT="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")"
gh attestation verify "release/codetruss-cli-${VERSION}.tgz" \
--repo "$GITHUB_REPOSITORY" \
--signer-workflow "$GITHUB_REPOSITORY/.github/workflows/release.yml" \
--source-ref "refs/tags/${RELEASE_TAG}" \
--source-digest "$TAG_COMMIT" \
--deny-self-hosted-runners
- name: Resolve the verified package identity
id: package
run: |
node - <<'NODE'
const { createHash } = require('node:crypto')
const { appendFileSync, readFileSync } = require('node:fs')
const pkg = require('./packages/cli/package.json')
const archiveName = `codetruss-cli-${pkg.version}.tgz`
const archive = readFileSync(`release/${archiveName}`)
const archiveSha256 = createHash('sha256').update(archive).digest('hex')
appendFileSync(process.env.GITHUB_OUTPUT, `archive_name=${archiveName}\n`)
appendFileSync(process.env.GITHUB_OUTPUT, `archive_sha256=${archiveSha256}\n`)
appendFileSync(process.env.GITHUB_OUTPUT, `package_version=${pkg.version}\n`)
NODE
- name: Install locked dependencies
run: pnpm install --frozen-lockfile
- name: Verify exact release bytes
run: |
pnpm build
pnpm release:verify
pnpm test:install
- name: Dry-run the exact npm package path
env:
ARCHIVE_NAME: ${{ steps.package.outputs.archive_name }}
run: |
npm publish "./release/${ARCHIVE_NAME}" \
--dry-run \
--access public \
--ignore-scripts \
--registry=https://registry.npmjs.org
- name: Require the verified archive to remain unchanged
env:
ARCHIVE_NAME: ${{ steps.package.outputs.archive_name }}
ARCHIVE_SHA256: ${{ steps.package.outputs.archive_sha256 }}
run: echo "${ARCHIVE_SHA256} release/${ARCHIVE_NAME}" | /usr/bin/sha256sum --check --strict
- name: Upload the exact verified package
id: handoff
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
path: release/${{ steps.package.outputs.archive_name }}
archive: false
if-no-files-found: error
retention-days: 1
- name: Require the handoff digest to match the package
env:
ARCHIVE_SHA256: ${{ steps.package.outputs.archive_sha256 }}
ARTIFACT_SHA256: ${{ steps.handoff.outputs.artifact-digest }}
run: test "$ARTIFACT_SHA256" = "$ARCHIVE_SHA256"
publish:
name: Publish verified package
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 10
environment: npm
permissions:
id-token: write
steps:
- name: Validate the verified handoff identity
env:
ARCHIVE_NAME: ${{ needs.verify.outputs.archive_name }}
ARCHIVE_SHA256: ${{ needs.verify.outputs.archive_sha256 }}
ARTIFACT_ID: ${{ needs.verify.outputs.artifact_id }}
PACKAGE_VERSION: ${{ needs.verify.outputs.package_version }}
run: |
[[ "$PACKAGE_VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]
test "$ARCHIVE_NAME" = "codetruss-cli-${PACKAGE_VERSION}.tgz"
[[ "$ARCHIVE_SHA256" =~ ^[0-9a-f]{64}$ ]]
[[ "$ARTIFACT_ID" =~ ^[0-9]+$ ]]
- name: Download the exact verified package
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ needs.verify.outputs.artifact_id }}
path: release
digest-mismatch: error
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- name: Verify the downloaded package identity
env:
ARCHIVE_NAME: ${{ needs.verify.outputs.archive_name }}
ARCHIVE_SHA256: ${{ needs.verify.outputs.archive_sha256 }}
PACKAGE_VERSION: ${{ needs.verify.outputs.package_version }}
run: |
test "$(find release -mindepth 1 -maxdepth 1 -type f | wc -l | tr -d ' ')" = "1"
test -z "$(find release -mindepth 1 -maxdepth 1 ! -type f -print -quit)"
test -f "release/${ARCHIVE_NAME}"
echo "${ARCHIVE_SHA256} release/${ARCHIVE_NAME}" | /usr/bin/sha256sum --check --strict
tar -xOf "release/${ARCHIVE_NAME}" package/package.json > release/package.json
PACKAGE_VERSION="$PACKAGE_VERSION" node - <<'NODE'
const manifest = require('./release/package.json')
if (manifest.name !== '@codetruss/cli' || manifest.version !== process.env.PACKAGE_VERSION) {
throw new Error(`unexpected package identity ${manifest.name}@${manifest.version}`)
}
const publishConfig = Object.entries(manifest.publishConfig ?? {})
if (publishConfig.length !== 1
|| publishConfig[0]?.[0] !== 'access'
|| publishConfig[0]?.[1] !== 'public') {
throw new Error('package publishConfig must contain only public access')
}
NODE
rm release/package.json
- name: Activate pinned npm
run: |
npm install --global --ignore-scripts --no-audit --no-fund npm@12.0.1
test "$(npm --version)" = "12.0.1"
- name: Publish with npm trusted publishing
env:
ARCHIVE_NAME: ${{ needs.verify.outputs.archive_name }}
run: |
npm publish "./release/${ARCHIVE_NAME}" \
--access public \
--ignore-scripts \
--provenance \
--registry=https://registry.npmjs.org