Releasing opnDossier#
This document describes the release process for opnDossier.
Table of Contents#
- Version Numbering
- Prerequisites
- Pre-release Checklist
- Creating a Release
- Post-release Verification
- Release Candidates
- Hotfix Process
- Troubleshooting
Version Numbering#
opnDossier follows Semantic Versioning 2.0.0:
| Version Component | When to Increment | Example |
|---|---|---|
| MAJOR (X.0.0) | Breaking changes to CLI interface, config format, or API | Removing a flag, changing output format |
| MINOR (0.X.0) | New features, backward-compatible additions | New audit plugin, new output format |
| PATCH (0.0.X) | Bug fixes, documentation, performance improvements | Fix parsing bug, typo fixes |
Pre-release Tags#
- Release Candidates:
v1.2.0-rc1,v1.2.0-rc2- Feature-complete, needs testing - Beta:
v1.2.0-beta.1- Feature incomplete, early testing - Alpha:
v1.2.0-alpha.1- Experimental, unstable
Prerequisites#
Required Tools#
Install these tools before creating a release:
# Install via mise (recommended - see .mise.toml)
mise install
# Or install manually:
# goreleaser - https://goreleaser.com/install/
brew install goreleaser/tap/goreleaser
# git-cliff - https://git-cliff.org/docs/installation
brew install git-cliff
# cosign v3 - https://docs.sigstore.dev/cosign/installation/
# Note: v3+ uses keyless signing by default with .sigstore.json bundles
brew install cosign
# cyclonedx-gomod (for SBOM) - https://github.com/CycloneDX/cyclonedx-gomod
go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
# go-licenses (for third-party notices) - https://github.com/google/go-licenses
go install github.com/google/go-licenses@latest
# quill (for macOS code signing, optional) - https://github.com/anchore/quill
# Only needed if you want to sign and notarize macOS binaries
curl -sSfL https://raw.githubusercontent.com/anchore/quill/main/install.sh | sh -s -- -b /usr/local/bin
Environment Variables (Optional)#
For signed releases, configure these environment variables:
# macOS Code Signing with Quill (optional)
# See: https://github.com/anchore/quill
export QUILL_SIGN_P12="path/to/certificate.p12" # or base64-encoded P12
export QUILL_SIGN_PASSWORD="certificate-password"
export QUILL_NOTARY_KEY="path/to/AuthKey_XXXXX.p8" # Apple API key
export QUILL_NOTARY_KEY_ID="XXXXXXXXXX" # Key ID from App Store Connect
export QUILL_NOTARY_ISSUER="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Issuer UUID
# Linux Package Signing (optional)
export RPM_SIGNING_KEY_FILE="path/to/rpm-key"
export DEB_SIGNING_KEY_FILE="path/to/deb-key"
export APK_SIGNING_KEY_FILE="path/to/apk-key"
Note
Cosign v3 uses keyless signing via Sigstore OIDC, so no signing keys are needed for artifact signatures when running in GitHub Actions.
GitHub Permissions#
The release workflow requires:
contents: write- Create releases and upload assetsid-token: write- SLSA provenance and Cosign keyless signing
GitHub Secrets#
For GPG signing of release artifacts, add these repository secrets:
| Secret | Description |
|---|---|
GPG_PRIVATE_KEY | Base64-encoded GPG private key (gpg --armor --export-secret-keys EMAIL | base64) |
GPG_PASSPHRASE | Passphrase for the GPG key |
Note
GPG signing is optional. If these secrets are not set, releases will still be created with Cosign signatures for checksums.
Pre-release Checklist#
Before creating a release, verify:
- All CI checks pass on
mainbranch - All issues/PRs for the milestone are closed
-
CHANGELOG.mdis up to date (or will be auto-generated) - Version references in code are correct (if any hardcoded)
- Documentation reflects new features/changes
- Breaking changes are documented
Verify CI Status#
# Check CI status for main branch
gh run list --branch main --limit 5
# View specific workflow run
gh run view <run-id>
Close Milestone#
# List open milestones
gh milestone list --state open
# Close milestone (goreleaser will also auto-close on release)
gh milestone edit <milestone-number> --state closed
Creating a Release#
Step 1: Validate Configuration#
# Check goreleaser configuration
goreleaser check
# Preview what would be built (no publish)
goreleaser release --snapshot --clean
# Check generated artifacts
ls -la dist/
Note
The release workflow automatically generates THIRD_PARTY_NOTICES via the just notices command as a GoReleaser before-hook. This file provides human-readable license attribution for all dependencies and is included in all distribution archives and packages. The formatting is controlled by the template at packaging/notices.tpl.
Step 2: Generate Changelog Preview#
# Preview changelog for unreleased commits
git-cliff --unreleased
# Preview full changelog
git-cliff --output /dev/stdout
Step 3: Create and Push Tag#
# Ensure you're on main with latest changes
git checkout main
git pull origin main
# Create annotated tag
git tag -a v1.2.0 -m "Release v1.2.0"
# Push tag to trigger release workflow
git push origin v1.2.0
Step 4: Create GitHub Release#
Option A: Via GitHub UI (Recommended)
- Go to Releases
- Click "Draft a new release"
- Select the tag you just pushed
- Click "Generate release notes" for auto-generated notes
- Review and edit the release notes
- Click "Publish release"
Option B: Via CLI
# Create release from tag (triggers workflow)
gh release create v1.2.0 \
--title "v1.2.0" \
--generate-notes
# Or with custom notes
gh release create v1.2.0 \
--title "v1.2.0" \
--notes-file RELEASE_NOTES.md
Step 5: Monitor Release Workflow#
# Watch the release workflow
gh run watch
# Or list recent workflow runs
gh run list --workflow=release.yml --limit 5
Post-release Verification#
After the release workflow completes:
Verify Artifacts#
# List release assets
gh release view v1.2.0
# Download and verify checksums
gh release download v1.2.0 --pattern "*checksums*"
sha256sum -c opnDossier_checksums.txt
Verify SLSA Provenance#
# Install slsa-verifier
brew install slsa-framework/tap/slsa-verifier
# Download provenance
gh release download v1.2.0 --pattern "*.intoto.jsonl"
# Verify provenance
slsa-verifier verify-artifact \
--provenance-path opnDossier-v1.2.0.intoto.jsonl \
--source-uri github.com/EvilBit-Labs/opnDossier \
--source-tag v1.2.0 \
opnDossier_checksums.txt
Verify Cosign Signatures (v3)#
# Download checksum file and its signature bundle
gh release download v1.2.0 --pattern "*checksums*"
gh release download v1.2.0 --pattern "*.sigstore.json"
# Verify signature with Cosign v3 (keyless, using Sigstore bundle format)
cosign verify-blob \
--certificate-identity "https://github.com/EvilBit-Labs/opnDossier/.github/workflows/release.yml@refs/tags/v1.2.0" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--bundle opnDossier_checksums.txt.sigstore.json \
opnDossier_checksums.txt
Note
Cosign v3 uses .sigstore.json bundle format instead of separate .sig and .pem files.
Verify GPG Signatures#
All release archives and packages are signed with the EvilBit Labs software signing key.
# Import the public key (one-time setup)
curl -sSL https://raw.githubusercontent.com/EvilBit-Labs/opnDossier/main/keys/software-signing.asc | gpg --import
# Or from a local clone
gpg --import keys/software-signing.asc
# Download an artifact and its signature
gh release download v1.2.0 --pattern "opnDossier_Linux_x86_64.tar.gz*"
# Verify the signature
gpg --verify opnDossier_Linux_x86_64.tar.gz.sig opnDossier_Linux_x86_64.tar.gz
Key details:
- Email:
software@evilbitlabs.io - Fingerprint:
138B FA78 8F37 7661 EA48 2C1D EFC6 F4CA BED2 2E8E - Key type: RSA 4096
- Expires: 2030-02-03
Test Installation#
# Test binary download and execution
gh release download v1.2.0 --pattern "*Darwin_arm64*"
tar -xzf opnDossier_Darwin_arm64.tar.gz
./opndossier --version
# Test package installation (Linux)
# Debian/Ubuntu
sudo dpkg -i opndossier_1.2.0_amd64.deb
opndossier --version
# RHEL/Fedora
sudo rpm -i opndossier-1.2.0-1.x86_64.rpm
opndossier --version
Release Candidates#
Use release candidates for significant releases that need broader testing.
Creating an RC#
# Tag release candidate
git tag -a v1.2.0-rc1 -m "Release candidate 1 for v1.2.0"
git push origin v1.2.0-rc1
# Create pre-release on GitHub
gh release create v1.2.0-rc1 \
--title "v1.2.0-rc1" \
--prerelease \
--generate-notes
Promoting RC to Release#
If the RC is stable:
# Tag the same commit as final release
git tag -a v1.2.0 -m "Release v1.2.0"
git push origin v1.2.0
# Create the final release
gh release create v1.2.0 --title "v1.2.0" --generate-notes
Hotfix Process#
For urgent fixes to a released version:
Step 1: Create Hotfix Branch#
# Branch from the release tag
git checkout -b hotfix/v1.2.1 v1.2.0
# Make the fix
# ... edit files ...
# Commit with conventional commit format
git commit -m "fix(parser): handle edge case in XML parsing"
Step 2: Create PR and Merge#
# Push hotfix branch
git push origin hotfix/v1.2.1
# Create PR targeting main
gh pr create --title "fix: critical parsing bug" --base main
# After review and merge, tag the release
git checkout main
git pull
git tag -a v1.2.1 -m "Hotfix release v1.2.1"
git push origin v1.2.1
Troubleshooting#
Common Issues#
goreleaser check fails#
# Validate YAML syntax
yamllint .goreleaser.yaml
# Check for deprecated options
goreleaser check --deprecated
Cosign signing fails#
# Verify cosign is configured for keyless signing
cosign version
# Test keyless signing locally
echo "test" | cosign sign-blob --yes - --bundle test.bundle
SLSA provenance fails#
Check the workflow logs:
gh run view <run-id> --log-failed
Manual Release (Emergency)#
If the automated workflow fails, you can release manually:
# Build locally
goreleaser release --clean
# Or skip certain steps
goreleaser release --clean --skip=sign
macOS Code Signing (Optional)#
macOS binaries can be signed and notarized using Quill, an open-source alternative to gon that works cross-platform.
Setup#
- Obtain an Apple Developer certificate and API key from App Store Connect
- Export the certificate as a P12 file
- Set the environment variables listed in Environment Variables
How It Works#
The goreleaser configuration includes a post-build hook for macOS:
- Snapshot builds: Ad-hoc signing only (no notarization)
- Release builds: Full signing and notarization with Apple
If QUILL_SIGN_P12 is not set, macOS signing is skipped entirely.
Release Artifacts#
Each release includes:
| Artifact | Description |
|---|---|
opnDossier_<OS>_<arch>.tar.gz | Binary archives (Linux, macOS, FreeBSD) with man page and completions |
opnDossier_<OS>_<arch>.zip | Binary archive (Windows) with THIRD_PARTY_NOTICES |
opndossier_<version>_amd64.deb | Debian/Ubuntu package with THIRD_PARTY_NOTICES in /usr/share/doc |
opndossier-<version>-1.x86_64.rpm | RHEL/Fedora package with THIRD_PARTY_NOTICES in /usr/share/doc |
opndossier_<version>_x86_64.apk | Alpine package with THIRD_PARTY_NOTICES |
opndossier-<version>-1-x86_64.pkg.tar.zst | Arch Linux package with THIRD_PARTY_NOTICES |
opnDossier_checksums.txt | SHA256 checksums for all artifacts |
opnDossier_checksums.txt.sigstore.json | Cosign v3 signature bundle |
*.sig | GPG detached signatures for archives/packages |
*.bom.json | Software Bill of Materials (CycloneDX SBOM) |
THIRD_PARTY_NOTICES | Human-readable license attribution for all dependencies |
Quick Release Checklist#
Copy-paste checklist for cutting a release. See sections above for details on each step.
Pre-flight#
- CI green on
main—gh run list --branch main --limit 5 -
just ci-checkpasses locally (lint, tests, race detector) - Milestone closed —
gh milestone list --state open, then close if exists - No uncommitted or unrelated changes on
main
Prepare#
- Generate changelog —
just changelog-version vX.Y.Z - Review
CHANGELOG.md— verify entries are correct and complete - Write or update
RELEASE_NOTES.md - Commit
RELEASE_NOTES.mdandCHANGELOG.mdtomain - Push to
main
Tag and Release#
- Ensure you are on
mainwith latest —git checkout main && git pull origin main - Create annotated tag —
git tag -a vX.Y.Z -m "Release vX.Y.Z" - Push tag —
git push origin vX.Y.Z - Create GitHub release —
gh release create vX.Y.Z --title "vX.Y.Z" --notes-file RELEASE_NOTES.md
Reminder: Always tag the commit on
main, never a feature branch head (see GOTCHAS.md #12.1).
Post-release Verification#
- Monitor workflow —
gh run watchorgh run list --workflow=release.yml - Verify artifacts —
gh release view vX.Y.Z - Verify cosign signature — download checksums +
.sigstore.json, runcosign verify-blob - Test binary — download, run
opndossier --version, confirm version - Verify Docker —
docker pull ghcr.io/evilbit-labs/opndossier:vX.Y.Z - Verify Homebrew cask updated (if
HOMEBREW_TAP_TOKENis set)