From afbbbaf878305ad9bdd59914914cef55ea67ef45 Mon Sep 17 00:00:00 2001 From: Cody Lee Date: Tue, 10 Feb 2026 11:41:33 -0600 Subject: [PATCH 1/3] feat(ci): add automatic Homebrew tap publishing Configure GoReleaser and GitHub Actions to automatically publish formula updates to DataDog/homebrew-pack on releases. This enables users to install via `brew install datadog/pack/pup` for easier distribution and updates. Changes: - .goreleaser.yml: Add brews configuration for homebrew-pack tap - .github/workflows/release.yml: Add HOMEBREW_TAP_TOKEN to env - README.md: Add Homebrew as primary installation method - CLAUDE.md: Update Quick Start with Homebrew instructions - docs/HOMEBREW_TAP_SETUP.md: Comprehensive setup guide with: - Prerequisites and repository requirements - Step-by-step PAT creation instructions - GitHub secret configuration - Testing procedure with pre-releases - Troubleshooting common issues - Security best practices Manual steps required: 1. Create fine-grained PAT with Contents: Read and Write on homebrew-pack 2. Add PAT as HOMEBREW_TAP_TOKEN secret to pup repository 3. Ensure homebrew-pack repository exists and is public See docs/HOMEBREW_TAP_SETUP.md for complete instructions. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/release.yml | 1 + .goreleaser.yml | 28 +++++ CLAUDE.md | 6 +- README.md | 13 +++ docs/HOMEBREW_TAP_SETUP.md | 186 ++++++++++++++++++++++++++++++++++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 docs/HOMEBREW_TAP_SETUP.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11003103..bc41ae43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,7 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} - name: Upload release artifacts uses: actions/upload-artifact@v6 diff --git a/.goreleaser.yml b/.goreleaser.yml index be265a2f..c2b79ae7 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -156,6 +156,34 @@ changelog: - title: "Other Changes" order: 999 +# Homebrew Tap +brews: + - name: pup + repository: + owner: DataDog + name: homebrew-pack + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + homepage: https://github.com/DataDog/pup + description: "Datadog API CLI - OAuth2 + API key authentication for Datadog APIs" + license: Apache-2.0 + folder: Formula + commit_author: + name: goreleaserbot + email: bot@goreleaser.com + commit_msg_template: "chore(formula): update {{ .ProjectName }} to {{ .Tag }}" + install: | + bin.install "pup" + test: | + system "#{bin}/pup", "version" + dependencies: + - name: go + type: optional + extra_install: | + # Generate shell completions + bash_completion.install "completions/pup.bash" => "pup" + zsh_completion.install "completions/pup.zsh" => "_pup" + fish_completion.install "completions/pup.fish" + # Announce releases announce: skip: false diff --git a/CLAUDE.md b/CLAUDE.md index 711f0def..562addbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,7 +15,11 @@ Go-based CLI wrapper for Datadog APIs. Provides OAuth2 + API key authentication ## Quick Start ```bash -# Clone and build +# Install via Homebrew (recommended) +brew tap datadog/pack +brew install pup + +# Or clone and build from source git clone https://github.com/DataDog/pup.git && cd pup go build -o pup . diff --git a/README.md b/README.md index ea8921a0..83182165 100644 --- a/README.md +++ b/README.md @@ -157,10 +157,23 @@ See [docs/COMMANDS.md](docs/COMMANDS.md) for detailed command reference. ## Installation +### Homebrew (macOS/Linux) + +```bash +brew tap datadog/pack +brew install pup +``` + +### Go Install + ```bash go install github.com/DataDog/pup@latest ``` +### Manual Download + +Download pre-built binaries from the [latest release](https://github.com/DataDog/pup/releases/latest). + ## Authentication Pup supports two authentication methods. **OAuth2 is preferred** and will be used automatically if you've logged in. diff --git a/docs/HOMEBREW_TAP_SETUP.md b/docs/HOMEBREW_TAP_SETUP.md new file mode 100644 index 00000000..8f0fb9e6 --- /dev/null +++ b/docs/HOMEBREW_TAP_SETUP.md @@ -0,0 +1,186 @@ +# Homebrew Tap Setup Guide + +This guide documents the setup required to enable automatic Homebrew formula publishing to the `DataDog/homebrew-pack` tap. + +## Overview + +When a new release is tagged, GoReleaser will automatically: +1. Build multi-platform binaries +2. Create a GitHub release +3. Generate and push a Homebrew formula to `DataDog/homebrew-pack` +4. Users can then install via: `brew install datadog/pack/pup` + +## Prerequisites + +### 1. Repository Setup + +The `DataDog/homebrew-pack` repository must: +- ✅ Exist at https://github.com/DataDog/homebrew-pack +- ✅ Be public (or have appropriate access configured) +- ✅ Have a `Formula/` directory (GoReleaser will create it if missing) +- ✅ Follow Homebrew tap naming conventions (`homebrew-*` prefix) + +### 2. GitHub Personal Access Token (PAT) + +You need to create a **Fine-grained Personal Access Token** with the following permissions: + +#### Token Permissions Required: +- **Repository access**: `DataDog/homebrew-pack` +- **Repository permissions**: + - Contents: `Read and Write` (to push formula updates) + - Metadata: `Read-only` (automatically granted) + +#### Creating the Token: + +1. Go to: https://github.com/settings/tokens?type=beta +2. Click **Generate new token** (Fine-grained) +3. Configure: + - **Token name**: `pup-homebrew-tap-publisher` + - **Expiration**: `No expiration` or `Custom` (recommend 1 year) + - **Repository access**: Select **Only select repositories** → Choose `DataDog/homebrew-pack` + - **Permissions**: Set `Contents` to `Read and Write` +4. Click **Generate token** +5. **Copy the token immediately** (you won't be able to see it again) + +### 3. Add Token as GitHub Secret + +Add the PAT to the `DataDog/pup` repository secrets: + +1. Go to: https://github.com/DataDog/pup/settings/secrets/actions +2. Click **New repository secret** +3. Configure: + - **Name**: `HOMEBREW_TAP_TOKEN` + - **Secret**: Paste the PAT created above +4. Click **Add secret** + +## Verification Checklist + +Before creating your first release with Homebrew tap publishing: + +- [ ] `DataDog/homebrew-pack` repository exists and is public +- [ ] Fine-grained PAT created with `Contents: Read and Write` on `homebrew-pack` +- [ ] `HOMEBREW_TAP_TOKEN` secret added to `DataDog/pup` repository +- [ ] Release workflow has `HOMEBREW_TAP_TOKEN` in env (already done in `.github/workflows/release.yml`) +- [ ] GoReleaser config includes `brews` section (already done in `.goreleaser.yml`) + +## Testing the Setup + +### Test with a Pre-release + +To test without affecting production: + +1. Create a pre-release tag: + ```bash + git tag -a v0.9.0-beta.1 -m "Test release for Homebrew tap" + git push origin v0.9.0-beta.1 + ``` + +2. Monitor the GitHub Actions workflow: + - Go to: https://github.com/DataDog/pup/actions + - Check the "Release" workflow run + - Verify GoReleaser successfully pushes to `homebrew-pack` + +3. Verify the formula was created: + - Check: https://github.com/DataDog/homebrew-pack/blob/main/Formula/pup.rb + - The formula should be auto-generated with version `0.9.0-beta.1` + +4. Test installation (optional): + ```bash + brew tap datadog/pack + brew install pup + pup version + ``` + +5. Clean up the test release if needed: + ```bash + git tag -d v0.9.0-beta.1 + git push origin :refs/tags/v0.9.0-beta.1 + ``` + +## First Production Release + +Once testing is successful: + +1. Create a production release tag: + ```bash + git tag -a v1.0.0 -m "Release v1.0.0" + git push origin v1.0.0 + ``` + +2. The workflow will: + - Build binaries for all platforms + - Create GitHub release with artifacts + - Push `pup.rb` formula to `DataDog/homebrew-pack` + +3. Users can install via: + ```bash + brew tap datadog/pack + brew install pup + ``` + +## Troubleshooting + +### Error: "failed to publish artifacts: formula: authentication required" + +**Cause**: The `HOMEBREW_TAP_TOKEN` is missing or invalid. + +**Solution**: +1. Verify the secret exists: https://github.com/DataDog/pup/settings/secrets/actions +2. Check the token hasn't expired +3. Verify token has `Contents: Read and Write` permissions on `homebrew-pack` + +### Error: "failed to publish artifacts: formula: repository not found" + +**Cause**: The `homebrew-pack` repository doesn't exist or is private. + +**Solution**: +1. Verify repository exists: https://github.com/DataDog/homebrew-pack +2. Ensure it's public or the PAT has access +3. Check the repository name is exactly `homebrew-pack` (case-sensitive) + +### Error: "failed to push formula: permission denied" + +**Cause**: The PAT doesn't have write permissions. + +**Solution**: +1. Recreate the PAT with `Contents: Read and Write` +2. Update the `HOMEBREW_TAP_TOKEN` secret + +### Formula not updating with new versions + +**Cause**: GoReleaser might be skipping the formula update. + +**Solution**: +1. Check the release workflow logs for errors +2. Ensure the tag matches the version pattern (e.g., `v1.2.3`) +3. Verify the `homebrew-pack` repository is not archived or locked + +## Maintenance + +### Token Expiration + +If you set an expiration date on the PAT: +1. Set a calendar reminder 1 week before expiration +2. Generate a new token with the same permissions +3. Update the `HOMEBREW_TAP_TOKEN` secret + +### Updating Formula Configuration + +To modify the formula (e.g., add dependencies, change test commands): +1. Edit `.goreleaser.yml` → `brews` section +2. Test with a pre-release tag +3. The formula will auto-update on the next release + +## Security Notes + +- ✅ Use **fine-grained PATs** (not classic tokens) - more secure and scoped +- ✅ Limit token access to **only** `DataDog/homebrew-pack` +- ✅ Set reasonable expiration dates (1 year recommended) +- ✅ Never commit tokens to the repository +- ✅ Rotate tokens periodically + +## References + +- [GoReleaser Homebrew Tap Documentation](https://goreleaser.com/customization/homebrew/) +- [Homebrew Tap Creation Guide](https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap) +- [GitHub Fine-grained PAT Documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-fine-grained-personal-access-token) From bd57b83a05f979aa879154cdbdf8a5b6b10ebf95 Mon Sep 17 00:00:00 2001 From: Cody Lee Date: Tue, 10 Feb 2026 14:20:06 -0600 Subject: [PATCH 2/3] refactor(ci): replace PAT with dd-octo-sts for Homebrew tap publishing Replace long-lived Personal Access Token with dd-octo-sts for more secure, short-lived token access to homebrew-pack repository. This eliminates the need for secret storage and manual token rotation. Security improvements: - Short-lived tokens (1 hour expiration, auto-revoked) - No credential storage required (OIDC federation) - Scoped to specific workflow and semantic version tags - Defense-in-depth with claim pattern validation Changes: - .github/workflows/release.yml: Add dd-octo-sts-action step to get token - docs/HOMEBREW_TAP_SETUP.md: Complete rewrite for dd-octo-sts approach - docs/homebrew-pack-trust-policy.yaml: Trust policy template for homebrew-pack Setup required: 1. Add trust policy to DataDog/homebrew-pack at .github/chainguard/pup-release.sts.yaml 2. Merge policy to default branch (no GitHub secrets needed!) See docs/HOMEBREW_TAP_SETUP.md for complete setup instructions. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/release.yml | 9 ++++++++- docs/homebrew-pack-trust-policy.yaml | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 docs/homebrew-pack-trust-policy.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc41ae43..2ddb7200 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,6 +36,13 @@ jobs: - name: Install syft (for SBOM generation) uses: anchore/sbom-action/download-syft@v0.22.2 + - name: Get Homebrew tap token via dd-octo-sts + uses: DataDog/dd-octo-sts-action@acaa02eee7e3bb0839e4272dacb37b8f3b58ba80 # v1.0.3 + id: octo-sts + with: + scope: DataDog/homebrew-pack + policy: pup-release + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: @@ -44,7 +51,7 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ steps.octo-sts.outputs.token }} - name: Upload release artifacts uses: actions/upload-artifact@v6 diff --git a/docs/homebrew-pack-trust-policy.yaml b/docs/homebrew-pack-trust-policy.yaml new file mode 100644 index 00000000..3cddf1b5 --- /dev/null +++ b/docs/homebrew-pack-trust-policy.yaml @@ -0,0 +1,23 @@ +# Trust policy for pup release workflow to push Homebrew formulas +# +# This file should be placed at: +# .github/chainguard/pup-release.sts.yaml +# +# in the DataDog/homebrew-pack repository + +issuer: https://token.actions.githubusercontent.com + +# Allow releases from semantic version tags (v1.2.3, v0.1.0, etc.) +subject_pattern: repo:DataDog/pup:ref:refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + +# Defense-in-depth: additional claim validation +claim_pattern: + repository: DataDog/pup + ref: refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + ref_type: tag + event_name: push + job_workflow_ref: DataDog/pup/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + +# Grant write access to push formula updates +permissions: + contents: write From c5a1ff0cac55664f339e9610c2a8bfc8172c92f5 Mon Sep 17 00:00:00 2001 From: Cody Lee Date: Tue, 10 Feb 2026 14:24:04 -0600 Subject: [PATCH 3/3] docs: add dd-octo-sts instructions and tag protection guidance Complete rewrite of HOMEBREW_TAP_SETUP.md to use dd-octo-sts instead of PATs: - Replaced PAT-based authentication with dd-octo-sts OIDC federation - Added comprehensive tag protection setup instructions (Step 3) - Included troubleshooting for tag protection scenarios - Added security best practices for release management - Documented both tag rulesets and protected environments approaches - Updated comparison table showing dd-octo-sts advantages Tag protection (recommended but optional): - Prevents unauthorized release creation - Aligns with dd-octo-sts security guardrails - Ensures privileged permissions only on protected refs Co-Authored-By: Claude Sonnet 4.5 --- docs/HOMEBREW_TAP_SETUP.md | 331 ++++++++++++++++++++++++++++--------- 1 file changed, 256 insertions(+), 75 deletions(-) diff --git a/docs/HOMEBREW_TAP_SETUP.md b/docs/HOMEBREW_TAP_SETUP.md index 8f0fb9e6..1ea177b3 100644 --- a/docs/HOMEBREW_TAP_SETUP.md +++ b/docs/HOMEBREW_TAP_SETUP.md @@ -1,14 +1,25 @@ # Homebrew Tap Setup Guide -This guide documents the setup required to enable automatic Homebrew formula publishing to the `DataDog/homebrew-pack` tap. +This guide documents the setup required to enable automatic Homebrew formula publishing to the `DataDog/homebrew-pack` tap using dd-octo-sts for secure, short-lived token access. ## Overview -When a new release is tagged, GoReleaser will automatically: -1. Build multi-platform binaries -2. Create a GitHub release -3. Generate and push a Homebrew formula to `DataDog/homebrew-pack` -4. Users can then install via: `brew install datadog/pack/pup` +When a new release is tagged, the release workflow will: +1. Request an OIDC token from GitHub Actions +2. Exchange it with dd-octo-sts for a scoped, short-lived GitHub token +3. Use GoReleaser to build binaries and push formula to `homebrew-pack` +4. Token automatically expires after 1 hour and is revoked after the workflow completes + +Users can then install via: `brew install datadog/pack/pup` + +## Why dd-octo-sts? + +**Security advantages over Personal Access Tokens (PATs):** +- ✅ **Short-lived tokens**: 1-hour expiration, auto-revoked after CI run +- ✅ **No credential storage**: No long-lived secrets to manage or rotate +- ✅ **Scoped access**: Limited to specific workflow, tags, and repository +- ✅ **Least privilege**: Only grants `contents: write` on `homebrew-pack` +- ✅ **Audit trail**: All token exchanges logged and traceable ## Prerequisites @@ -17,51 +28,149 @@ When a new release is tagged, GoReleaser will automatically: The `DataDog/homebrew-pack` repository must: - ✅ Exist at https://github.com/DataDog/homebrew-pack - ✅ Be public (or have appropriate access configured) -- ✅ Have a `Formula/` directory (GoReleaser will create it if missing) +- ✅ Have the trust policy merged to the default branch - ✅ Follow Homebrew tap naming conventions (`homebrew-*` prefix) -### 2. GitHub Personal Access Token (PAT) +### 2. Tag Protection (Recommended) + +**Strongly recommended** for security best practices: +- ✅ Protect version tags (`v*.*.*`) to prevent unauthorized releases +- ✅ Aligns with dd-octo-sts security guardrails (privileged permissions on protected refs) +- ✅ Ensures only authorized users can create releases + +See [Step 3: Protect Release Tags](#step-3-protect-release-tags-recommended) below for setup instructions. + +## Setup Instructions + +### Step 1: Add Trust Policy to homebrew-pack + +1. Clone the `homebrew-pack` repository: + ```bash + git clone https://github.com/DataDog/homebrew-pack.git + cd homebrew-pack + ``` + +2. Create the trust policy directory if it doesn't exist: + ```bash + mkdir -p .github/chainguard + ``` + +3. Create `.github/chainguard/pup-release.sts.yaml` with the following content: + ```yaml + # Trust policy for pup release workflow to push Homebrew formulas + issuer: https://token.actions.githubusercontent.com + + # Allow releases from semantic version tags (v1.2.3, v0.1.0, etc.) + subject_pattern: repo:DataDog/pup:ref:refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + + # Defense-in-depth: additional claim validation + claim_pattern: + repository: DataDog/pup + ref: refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + ref_type: tag + event_name: push + job_workflow_ref: DataDog/pup/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+ + + # Grant write access to push formula updates + permissions: + contents: write + ``` + + **Note**: A copy of this policy is available at `docs/homebrew-pack-trust-policy.yaml` in the pup repository. + +4. Commit and create a pull request: + ```bash + git checkout -b add-pup-release-policy + git add .github/chainguard/pup-release.sts.yaml + git commit -m "feat(policy): add trust policy for pup release workflow" + git push -u origin add-pup-release-policy + gh pr create --title "feat(policy): add trust policy for pup release workflow" \ + --body "Adds dd-octo-sts trust policy to allow pup release workflow to push formula updates securely using short-lived tokens." + ``` + +5. **Wait for Trust Policy Validation check to pass** (automated check by dd-octo-sts) + +6. **Merge the PR to the default branch** (policy must be on default branch to work) + +### Step 2: Verify pup Workflow Configuration + +The `pup` repository workflow is already configured (see `.github/workflows/release.yml`): + +```yaml +- name: Get Homebrew tap token via dd-octo-sts + uses: DataDog/dd-octo-sts-action@acaa02eee7e3bb0839e4272dacb37b8f3b58ba80 # v1.0.3 + id: octo-sts + with: + scope: DataDog/homebrew-pack + policy: pup-release -You need to create a **Fine-grained Personal Access Token** with the following permissions: +- name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ steps.octo-sts.outputs.token }} +``` -#### Token Permissions Required: -- **Repository access**: `DataDog/homebrew-pack` -- **Repository permissions**: - - Contents: `Read and Write` (to push formula updates) - - Metadata: `Read-only` (automatically granted) +**No GitHub secrets required!** The workflow uses OIDC federation automatically. -#### Creating the Token: +### Step 3: Protect Release Tags (Recommended) -1. Go to: https://github.com/settings/tokens?type=beta -2. Click **Generate new token** (Fine-grained) -3. Configure: - - **Token name**: `pup-homebrew-tap-publisher` - - **Expiration**: `No expiration` or `Custom` (recommend 1 year) - - **Repository access**: Select **Only select repositories** → Choose `DataDog/homebrew-pack` - - **Permissions**: Set `Contents` to `Read and Write` -4. Click **Generate token** -5. **Copy the token immediately** (you won't be able to see it again) +**Why protect tags?** +- Prevents unauthorized release creation +- Aligns with dd-octo-sts best practice: privileged permissions on protected refs +- Ensures only designated users/teams can trigger releases -### 3. Add Token as GitHub Secret +**How to set up tag protection:** -Add the PAT to the `DataDog/pup` repository secrets: +1. Go to: https://github.com/DataDog/pup/settings/rules/new -1. Go to: https://github.com/DataDog/pup/settings/secrets/actions -2. Click **New repository secret** -3. Configure: - - **Name**: `HOMEBREW_TAP_TOKEN` - - **Secret**: Paste the PAT created above -4. Click **Add secret** +2. Create a **Tag ruleset**: + - **Ruleset name**: `Protect Release Tags` + - **Enforcement status**: Active + - **Target**: Tags + - **Tag name pattern**: `v[0-9]*.[0-9]*.[0-9]*` + +3. **Configure protections**: + - ✅ **Restrict creations**: Check this box + - Add authorized users/teams who can create releases + - Example: Add release engineers, maintainers team + - ✅ **Restrict deletions**: Check this box (prevent accidental deletion) + - ✅ **Require signed commits**: Optional but recommended + +4. **Add bypass list** (users/teams who can create releases): + - Add your GitHub username + - Add release maintainers + - Consider creating a dedicated "releases" team + +5. **Save the ruleset** + +**Alternative: Protected Environments** (simpler but less granular) + +If tag rulesets are too complex, use a protected environment: + +1. Go to: https://github.com/DataDog/pup/settings/environments/new +2. Create environment named `release` +3. Add required reviewers +4. Update workflow to use environment: + ```yaml + jobs: + goreleaser: + runs-on: ubuntu-latest + environment: release # Add this line + ``` + +**Note**: The setup works without tag protection, but it's strongly recommended for production releases. ## Verification Checklist Before creating your first release with Homebrew tap publishing: - [ ] `DataDog/homebrew-pack` repository exists and is public -- [ ] Fine-grained PAT created with `Contents: Read and Write` on `homebrew-pack` -- [ ] `HOMEBREW_TAP_TOKEN` secret added to `DataDog/pup` repository -- [ ] Release workflow has `HOMEBREW_TAP_TOKEN` in env (already done in `.github/workflows/release.yml`) -- [ ] GoReleaser config includes `brews` section (already done in `.goreleaser.yml`) +- [ ] Trust policy merged to default branch in `homebrew-pack` +- [ ] Trust Policy Validation check passed on the policy PR +- [ ] Release workflow in `pup` includes dd-octo-sts-action step +- [ ] GoReleaser config includes `brews` section (`.goreleaser.yml`) +- [ ] (Recommended) Release tags protected via repository rulesets ## Testing the Setup @@ -69,32 +178,39 @@ Before creating your first release with Homebrew tap publishing: To test without affecting production: -1. Create a pre-release tag: +1. **Ensure the trust policy is merged to the default branch** in `homebrew-pack` + +2. Create a pre-release tag in the `pup` repository: ```bash + cd /path/to/pup git tag -a v0.9.0-beta.1 -m "Test release for Homebrew tap" git push origin v0.9.0-beta.1 ``` -2. Monitor the GitHub Actions workflow: + **Note**: If you protected tags, ensure you have permission to create them. + +3. Monitor the GitHub Actions workflow: - Go to: https://github.com/DataDog/pup/actions - Check the "Release" workflow run + - Verify the "Get Homebrew tap token via dd-octo-sts" step succeeds - Verify GoReleaser successfully pushes to `homebrew-pack` -3. Verify the formula was created: +4. Verify the formula was created: - Check: https://github.com/DataDog/homebrew-pack/blob/main/Formula/pup.rb - The formula should be auto-generated with version `0.9.0-beta.1` -4. Test installation (optional): +5. Test installation (optional): ```bash brew tap datadog/pack brew install pup pup version ``` -5. Clean up the test release if needed: +6. Clean up the test release if needed: ```bash git tag -d v0.9.0-beta.1 git push origin :refs/tags/v0.9.0-beta.1 + # Manually delete the GitHub release if created ``` ## First Production Release @@ -108,6 +224,7 @@ Once testing is successful: ``` 2. The workflow will: + - Get short-lived token via dd-octo-sts - Build binaries for all platforms - Create GitHub release with artifacts - Push `pup.rb` formula to `DataDog/homebrew-pack` @@ -120,67 +237,131 @@ Once testing is successful: ## Troubleshooting -### Error: "failed to publish artifacts: formula: authentication required" +### Error: "failed to exchange OIDC token" -**Cause**: The `HOMEBREW_TAP_TOKEN` is missing or invalid. +**Cause**: Trust policy not found or not on default branch. **Solution**: -1. Verify the secret exists: https://github.com/DataDog/pup/settings/secrets/actions -2. Check the token hasn't expired -3. Verify token has `Contents: Read and Write` permissions on `homebrew-pack` +1. Verify policy exists at `.github/chainguard/pup-release.sts.yaml` in `homebrew-pack` +2. Ensure it's merged to the default branch (usually `main`) +3. Check the policy filename exactly matches (`.sts.yaml` not `.sts.yml`) -### Error: "failed to publish artifacts: formula: repository not found" +### Error: "OIDC token validation failed" -**Cause**: The `homebrew-pack` repository doesn't exist or is private. +**Cause**: Claims in OIDC token don't match trust policy patterns. **Solution**: -1. Verify repository exists: https://github.com/DataDog/homebrew-pack -2. Ensure it's public or the PAT has access -3. Check the repository name is exactly `homebrew-pack` (case-sensitive) +1. Check the dd-octo-sts-action step logs - it prints claims on failure +2. Verify the tag matches pattern: `v[0-9]+\.[0-9]+\.[0-9]+` (e.g., `v1.2.3`) +3. Ensure workflow file is `.github/workflows/release.yml` (not renamed) +4. Compare printed claims against `claim_pattern` in trust policy -### Error: "failed to push formula: permission denied" +### Error: "insufficient permissions" or "permission denied" -**Cause**: The PAT doesn't have write permissions. +**Cause**: Trust policy doesn't grant required permissions or tags are protected. **Solution**: -1. Recreate the PAT with `Contents: Read and Write` -2. Update the `HOMEBREW_TAP_TOKEN` secret +1. Verify trust policy includes: `permissions: { contents: write }` +2. Ensure policy is on the default branch +3. If tags are protected, verify you're in the bypass list +4. Wait a few minutes after merging - policy cache may need to refresh + +### Error: "workflow not found" or "policy not found" + +**Cause**: Incorrect scope or policy name in workflow. + +**Solution**: +1. Verify `scope: DataDog/homebrew-pack` in workflow +2. Verify `policy: pup-release` matches filename (without `.sts.yaml`) +3. Check for typos in repository owner/name + +### Error: "resource not accessible by integration" when pushing tags + +**Cause**: Tag protection enabled but you're not in the bypass list. + +**Solution**: +1. Go to: https://github.com/DataDog/pup/settings/rules +2. Find the "Protect Release Tags" ruleset +3. Add your username to the bypass list +4. Or temporarily disable the ruleset for testing ### Formula not updating with new versions -**Cause**: GoReleaser might be skipping the formula update. +**Cause**: Previous step might have failed silently. **Solution**: -1. Check the release workflow logs for errors +1. Check full workflow logs for dd-octo-sts and GoReleaser steps 2. Ensure the tag matches the version pattern (e.g., `v1.2.3`) -3. Verify the `homebrew-pack` repository is not archived or locked +3. Verify GoReleaser config includes `brews` section +4. Check if `homebrew-pack` repository has any branch protection rules blocking pushes -## Maintenance +## Policy Maintenance -### Token Expiration +### Updating the Trust Policy -If you set an expiration date on the PAT: -1. Set a calendar reminder 1 week before expiration -2. Generate a new token with the same permissions -3. Update the `HOMEBREW_TAP_TOKEN` secret +To modify the trust policy (e.g., change permissions, add constraints): -### Updating Formula Configuration +1. Edit `.github/chainguard/pup-release.sts.yaml` in `homebrew-pack` +2. Create a PR with the changes +3. Wait for Trust Policy Validation check to pass +4. Merge to default branch +5. Changes take effect immediately for new workflow runs -To modify the formula (e.g., add dependencies, change test commands): -1. Edit `.goreleaser.yml` → `brews` section -2. Test with a pre-release tag -3. The formula will auto-update on the next release +### Common Policy Updates + +**Allow pre-release tags** (e.g., `v1.0.0-beta.1`): +```yaml +subject_pattern: repo:DataDog/pup:ref:refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)? +claim_pattern: + ref: refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)? +``` + +**Restrict to specific major versions**: +```yaml +subject_pattern: repo:DataDog/pup:ref:refs/tags/v1\.[0-9]+\.[0-9]+ +``` + +**Add workflow approval via protected environment**: +```yaml +claim_pattern: + environment: release # Add this line +``` ## Security Notes -- ✅ Use **fine-grained PATs** (not classic tokens) - more secure and scoped -- ✅ Limit token access to **only** `DataDog/homebrew-pack` -- ✅ Set reasonable expiration dates (1 year recommended) -- ✅ Never commit tokens to the repository -- ✅ Rotate tokens periodically +- ✅ **No secrets to rotate**: Tokens are short-lived and auto-revoked +- ✅ **Scoped access**: Policy restricts to specific workflow and tags +- ✅ **Audit trail**: All token exchanges logged in dd-octo-sts service +- ✅ **Defense in depth**: Multiple claim validations (`subject_pattern` + `claim_pattern`) +- ✅ **Protected refs recommended**: Tag protection adds extra security layer + +### Security Best Practices + +1. **Protect version tags**: Use repository rulesets to control who can create releases +2. **Keep patterns specific**: Avoid overly broad regex patterns in trust policies +3. **Review policy changes**: Always review trust policy PRs carefully +4. **Monitor workflow runs**: Check dd-octo-sts step logs for anomalies +5. **Least privilege**: Only grant minimum required permissions +6. **Regular audits**: Periodically review who has release tag creation permissions ## References +- [dd-octo-sts User Guide (Confluence)](https://datadoghq.atlassian.net/wiki/spaces/SECENG/pages/5138645099) +- [dd-octo-sts GitHub Action](https://github.com/DataDog/dd-octo-sts-action) +- [GitHub Tag Protection Rulesets](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/managing-rulesets-for-a-repository) - [GoReleaser Homebrew Tap Documentation](https://goreleaser.com/customization/homebrew/) - [Homebrew Tap Creation Guide](https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap) -- [GitHub Fine-grained PAT Documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-fine-grained-personal-access-token) +- Slack: [#sdlc-security](https://dd.enterprise.slack.com/archives/C027P1CK07N) + +## Comparison: dd-octo-sts vs PAT + +| Feature | dd-octo-sts | Personal Access Token | +|---------|-------------|----------------------| +| Token lifetime | 1 hour, auto-revoked | Indefinite until manually revoked | +| Secret storage | None (OIDC federation) | Requires GitHub secret | +| Scope | Workflow + tag specific | Broad access | +| Rotation | Automatic | Manual | +| Audit trail | Complete | Limited | +| Setup complexity | Medium (trust policy + tag protection) | Simple (create + add secret) | +| Security posture | ✅ Excellent | ⚠️ Acceptable | +| Recommended for | Datadog repos | External/simple use cases |