Package & Checklist
Verify Before Release
Release workflows publish to public registries. Verify artifact checksums, provenance generation, and signature verification instructions before tagging releases. Published packages cannot be deleted.
NPM Package Release¶
registry-url: 'https://npm.pkg.github.com'
scope: '@${{ github.repository_owner }}'
- run: npm ci
- run: npm run build
# SECURITY: Update package.json for GitHub Packages
- name: Configure package for GitHub Packages
run: |
jq '.name = "@${{ github.repository_owner }}/" + .name' package.json > package.json.tmp
mv package.json.tmp package.json
- name: Publish to GitHub Packages
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Job 3: Create GitHub release
release:
needs: [publish-npm, publish-github]
runs-on: ubuntu-latest
environment: production
permissions:
contents: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Create GitHub release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
with:
generate_release_notes: true
body: |
## NPM Package
Install via npm:
```bash
npm install ${{ github.event.repository.name }}@${{ github.ref_name }}
```
Install via GitHub Packages:
```bash
npm install @${{ github.repository_owner }}/${{ github.event.repository.name }}@${{ github.ref_name }}
```
### Provenance Verification
Verify package provenance:
```bash
npm audit signatures
```
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Provenance: npm CLI automatically generates and publishes provenance statements when --provenance flag used.
Security Checklist¶
Use this checklist to verify your release workflow follows security best practices.
Release Security¶
- [ ] Releases triggered only by version tags (
v*.*.*) - [ ] Tag signatures verified if commit signing enforced
- [ ] Environment protection with approval gate for production releases
- [ ] Pre-release flag set for non-stable versions (beta, rc)
- [ ] Release notes auto-generated from commit history
- [ ] Checksums generated for all release artifacts
Action Security¶
- [ ] All third-party actions pinned to full SHA-256 commit hashes
- [ ] Version comments added for human readability (
# vX.Y.Z) - [ ] Dependabot configured to update action pins
- [ ] Actions from verified publishers or GitHub-maintained only
- [ ] SLSA generator used for L3 provenance (if applicable)
Permission Security¶
- [ ] Workflow-level permissions set to minimal (
contents: read) - [ ]
id-token: writeonly on jobs that sign/attest - [ ]
attestations: writeonly on jobs that create attestations - [ ]
contents: writeonly on release creation job - [ ]
packages: writeonly on container/package publish jobs - [ ] No
permissions: write-allor default write permissions
Artifact Security¶
- [ ] Artifact attestations created for all release artifacts
- [ ] SLSA provenance generated (L2 minimum, L3 preferred)
- [ ] Checksums (SHA-256) generated for artifact integrity
- [ ] Artifacts uploaded with long retention (90 days for releases)
- [ ] SBOM generated for containers and dependencies
- [ ] Container images scanned for vulnerabilities before release
Signing and Verification¶
- [ ] Artifacts signed with OIDC (keyless signing)
- [ ] Container images signed with Cosign
- [ ] NPM packages published with
--provenanceflag - [ ] Signatures pushed to transparency log (Rekor)
- [ ] Verification instructions included in release notes
- [ ] Post-release verification job validates signatures
Distribution Security¶
- [ ] Container images pushed to GHCR or cloud-native artifact registries
- [ ] Multi-architecture manifests created for containers
- [ ] NPM packages published to both npm and GitHub Packages
- [ ] Registry authentication via OIDC (no long-lived tokens)
- [ ] Package scopes configured correctly
- [ ] Public packages marked as
--access public
Common Mistakes and Fixes¶
Mistake 1: Missing Attestations Permissions¶
Bad:
# DANGER: Attestation creation will fail without id-token and attestations permissions
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/attest-build-provenance@v1
with:
subject-path: 'dist/*'
Good:
# SECURITY: Grant id-token and attestations permissions for signing
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Generate OIDC tokens for signing
attestations: write # Create attestations
steps:
- uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: 'dist/*'
Mistake 2: Unverified Release Artifacts¶
Bad:
# DANGER: No checksum verification before release
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- uses: softprops/action-gh-release@v0.1.15
with:
files: 'dist/*'
Good:
# SECURITY: Verify checksums before releasing artifacts
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: release-artifacts
path: dist/
# SECURITY: Verify artifact integrity
- name: Verify checksums
run: |
cd dist/
sha256sum -c SHA256SUMS.txt
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
with:
files: 'dist/*'
Mistake 3: Container Images Without Provenance¶
Bad:
# DANGER: No provenance or SBOM for container image
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build container
run: |
buildah build --tag ghcr.io/org/image:latest .
buildah push ghcr.io/org/image:latest
Good:
# SECURITY: Enable provenance and SBOM for supply chain security
jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
id-token: write
attestations: write
steps:
- name: Build container with buildah
id: build
run: |
IMAGE="ghcr.io/org/image:${{ github.ref_name }}"
# Build with OCI-compliant tooling
buildah build \
--tag "${IMAGE}" \
--label org.opencontainers.image.version="${{ github.ref_name }}" \
.
# Push to registry
buildah push "${IMAGE}"
# Get digest for attestation
DIGEST=$(buildah inspect "${IMAGE}" | jq -r '.Digest')
echo "digest=${DIGEST}" >> $GITHUB_OUTPUT
# SECURITY: Attest container with GitHub attestations
- uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-name: ghcr.io/org/image
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
Mistake 4: NPM Publish Without Provenance¶
Bad:
# DANGER: NPM package published without provenance
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Good:
# SECURITY: Publish with provenance for supply chain transparency
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # Generate provenance
steps:
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
with:
registry-url: 'https://registry.npmjs.org'
# SECURITY: --provenance flag generates and publishes provenance
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Mistake 5: No Environment Protection for Releases¶
Bad:
# DANGER: No approval gate for production releases
on:
push:
tags: ['v*.*.*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: softprops/action-gh-release@v0.1.15
Good:
# SECURITY: Require approval before production releases
on:
push:
tags: ['v*.*.*']
jobs:
release:
runs-on: ubuntu-latest
# SECURITY: Environment protection with required reviewers
environment:
name: production
url: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}
permissions:
contents: write
steps:
- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
with:
generate_release_notes: true
Related Patterns¶
- Action Pinning: SHA pinning patterns and Dependabot configuration
- Token Permissions: GITHUB_TOKEN permission templates for all workflow types
- Environment Protection: Deployment gates and approval workflows
- Secret Management: OIDC patterns for keyless signing and cloud authentication
- Third-Party Actions: Security review of release-related actions
Summary¶
Secure release workflows require comprehensive attestation and verification:
- Pin all actions to SHA-256 hashes with version comments
- Minimize permissions with
id-token: writeandattestations: writeonly where needed - Generate attestations for all release artifacts (binaries, containers, packages)
- Create SLSA provenance at L2 minimum, L3 preferred for critical releases
- Sign artifacts with keyless signing (Cosign, npm provenance)
- Verify artifacts post-release with checksums and attestation verification
- Require approvals via environment protection for production releases
- Include verification instructions in release notes for users
Copy these templates as starting points for your release workflows. Adjust signing methods and distribution channels based on your artifact types and security requirements.