Last Updated: 2025-10-03
Status: Active
This document describes the GitHub Actions CI/CD pipeline for Ten Second Tom. The pipeline consists of two workflows that automate testing, building, and distributing the application across multiple platforms:
- PR Validation (
pr-validation.yml): Validates pull requests with build, test, and coverage checks - Release (
release.yml): Creates releases, builds binaries, and publishes to package managers
The following secrets must be configured in the repository settings (Settings → Secrets and variables → Actions):
HOMEBREW_TAP_TOKEN
- Purpose: Personal access token for pushing formula updates to Homebrew tap repository
- Scope:
repo(Full control of private repositories) - Setup:
- Go to GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click "Generate new token"
- Set name: "Homebrew Tap Token for Ten Second Tom"
- Set expiration: 1 year (or custom)
- Select repository access: Only select repositories →
sirkirby/ten-second-tom-homebrew(your tap) - Set repository permissions:
- Contents: Read and write
- Metadata: Read-only
- Generate token and copy immediately (won't be shown again)
- Add to repository secrets as
HOMEBREW_TAP_TOKEN - Important: Add the secret to the
productionenvironment (see Environment Setup below)
WINGET_TOKEN (Not yet implemented)
- Purpose: Token for creating pull requests to microsoft/winget-pkgs repository
- Status: Manual process documented in release workflow; automation planned for Phase 2
CHOCOLATEY_API_KEY (Not yet implemented)
- Purpose: API key for publishing packages to chocolatey.org
- Status: Manual process documented in release workflow; automation planned for Phase 2
The release workflow uses GitHub Environments to add manual approval gates for production deployments.
production
- Purpose: Requires manual approval before publishing to Homebrew
- Setup:
- Go to repository Settings → Environments
- Click "New environment"
- Name:
production(must match exactly) - Click "Configure environment"
- Protection Rules:
- ✅ Enable "Required reviewers"
- Add reviewer(s): Repository maintainers (e.g., @sirkirby)
- Set wait timer: 0 minutes (immediate notification)
- Environment Secrets:
- Add
HOMEBREW_TAP_TOKENsecret here (same value as repository secret)
- Add
- Save protection rules
Workflow Configuration:
publish-homebrew:
environment:
name: production
# Job will pause here until approvedVS Code Linter Warning: You may see a warning "Value 'production' is not valid" in VS Code. This is expected - the YAML linter cannot access your GitHub repository configuration to validate environment names. The workflow will execute correctly despite this warning.
Why Use Environments?
- Prevents accidental publication to Homebrew
- Allows final review of release notes before publishing
- Provides audit trail of who approved each release
- Can be bypassed for hotfixes by maintainers
Approval Flow:
- Release workflow creates GitHub release automatically
- Workflow pauses at Homebrew publication job
- GitHub sends notification to configured reviewers
- Reviewer checks release notes and approves/rejects
- Upon approval, Homebrew formula is published automatically
File: .github/workflows/pr-validation.yml
Trigger: Pull requests targeting main branch
Purpose: Validate code quality before merging
Jobs:
- Select Runner: Detect available self-hosted Linux runners or fall back to GitHub-hosted runners
- Build: Compile code with zero warnings
- Test: Run all unit and integration tests
- Coverage: Prevent coverage regression, track line coverage
Coverage Strategy:
- Primary Metric: Line coverage (% of executable lines hit by tests)
- Regression Prevention: PRs cannot decrease coverage by more than 0.5 percentage points
- No Absolute Threshold: Focus on preventing regression, not arbitrary targets
- Other Metrics Tracked: Branch coverage, method coverage (in reports, not enforced)
Why Line Coverage?
- Most widely understood and standardized metric
- Easier to reason about than branch or method coverage
- Industry standard for baseline quality gates
- Branch/method coverage available in detailed reports for deeper analysis
Performance Targets:
- Build job: ≤2 minutes
- Test job: ≤5 minutes
- Coverage job: ≤3 minutes
- Total: ≤10 minutes
Artifacts:
- Test results (TRX format, 7 days retention)
- Coverage reports (HTML/XML with line/branch/method metrics, 30 days retention)
Coverage Diff Comments: Automatically posts PR comment when coverage changes by ≥1 percentage point
File: .github/workflows/release.yml
Trigger:
- Push of semantic version tags (e.g.,
v1.2.3) - Manual dispatch via Actions UI (for testing)
Purpose: Build release binaries, create GitHub releases, and publish to package managers
Manual Testing: Supports workflow_dispatch with inputs:
tag: Version to release (e.g.,v0.0.1-test)skip_homebrew: Skip Homebrew publication (for PR testing)dry_run: Skip release creation, logs only
Jobs:
- Validate Version: Extract version from tag, validate format, check for duplicates, ensure tag is on main branch
- Build Release Artifacts: Build self-contained executables for all platforms with release version embedded
- Create GitHub Release: Package binaries, generate release notes, upload to GitHub releases
- Publish to Homebrew: Create Homebrew bottles (pre-built binaries), upload to release, update tap formula
- Document Winget: Generate Winget manifest template, create issue for manual publication (Phase 2: automate)
- Document Chocolatey: Generate Chocolatey nuspec template, create issue for manual publication (Phase 2: automate)
Build Configuration:
- Runtime: Self-contained (includes .NET runtime)
- Single File: Executable + embedded dependencies
- Trimming: Assembly-level IL trimming
- Size Target: <50MB per executable (enforced by workflow)
- Version Embedding: Release version embedded in binary metadata
Artifacts (90 days retention):
ten-second-tom-osx-x64-release: macOS Intel executable + config + native libsten-second-tom-osx-arm64-release: macOS Apple Silicon executable + config + native libsten-second-tom-win-x64-release: Windows executable + config + native libs
Platforms:
- macOS x64 (
osx-x64): Intel-based Macs (macOS 10.15+) - macOS ARM64 (
osx-arm64): Apple Silicon Macs (M1/M2/M3+) - Windows x64 (
win-x64): 64-bit Windows (Windows 10+)
Performance Targets:
- Validate version: <2 minutes
- Build artifacts (parallel): ≤10 minutes
- Create release: ≤2 minutes
- Homebrew publication: ≤5 minutes (excluding approval wait)
- Documentation jobs: <1 minute each
- Total: ≤15 minutes (excluding approval)
The PR validation workflow includes automatic detection of self-hosted Linux runners with fallback to GitHub-hosted runners.
Select Runner Job:
- Queries GitHub API for available self-hosted runners
- Checks for online Linux runners with
self-hostedlabel - Falls back to
ubuntu-latestif none found or API unavailable - Requires
RUNNER_QUERY_TOKENsecret (optional, falls back on failure)
Benefits:
- Faster builds on dedicated hardware
- Cost savings for high-frequency workflows
- Graceful degradation to cloud runners
Setup (optional):
- Configure self-hosted runner with Linux OS
- Add
self-hostedlabel to runner - Create fine-grained token with
actions: readpermission - Add as
RUNNER_QUERY_TOKENrepository secret
The release workflow requires manual approval before publishing to Homebrew:
- Tag Version: Push semantic version tag (e.g.,
git tag v1.0.0 && git push origin v1.0.0) - Validation: Workflow validates version format, checks for duplicates, ensures tag is on main branch
- Build Artifacts: Builds self-contained executables for all platforms with release version embedded
- GitHub Release: Creates release with binaries, checksums, and auto-generated release notes
- Approval Request: GitHub sends notification to reviewers configured in production environment
- Manual Review: Reviewer checks release notes and approves/rejects deployment
- Homebrew Publication: Upon approval, creates bottles and updates tap formula automatically
- Documentation: Creates GitHub issues for manual Winget/Chocolatey publication
For PR testing without creating actual releases:
# Go to Actions → Release → Run workflow
# - Branch: your-pr-branch
# - Tag: v0.0.1-test
# - Skip Homebrew: true
# - Dry run: trueThis allows testing the workflow logic without publishing to external services.
- ✅ Homebrew: Fully automated with bottle support and approval gate
- 📋 Winget: Manual publication with generated manifests (Phase 2: automate)
- 📋 Chocolatey: Manual publication with generated packages (Phase 2: automate)
To enable automated Homebrew publication with fast binary installations (bottles), you must create and configure a Homebrew tap repository. The release workflow automatically creates bottles, uploads them to GitHub releases, and updates the formula.
Bottles are pre-compiled binaries that Homebrew can install without building from source. This provides:
- Fast Installation: Users download ready-to-use binaries instead of compiling
- Consistent Builds: All users get identical binaries
- Better UX: No need for build tools or compilation wait time
- Architecture Support: Separate bottles for Intel and Apple Silicon Macs
- GitHub account (tap repository owner: sirkirby)
- macOS machine for local testing (optional but recommended)
- Homebrew installed locally for testing (
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)")
The tap repository follows Homebrew's standard naming convention:
-
Create a new GitHub repository:
- Name:
homebrew-ten-second-tom(must start withhomebrew-prefix per Homebrew conventions) - Visibility: Public (required for GitHub Packages and Homebrew)
- Description: "Homebrew tap for Ten Second Tom CLI"
- Initialize: Add a README.md explaining this is a Homebrew tap
- Name:
-
Repository URL:
https://github.com/sirkirby/homebrew-ten-second-tom -
Users will tap it as:
brew tap sirkirby/ten-second-tom(Homebrew strips thehomebrew-prefix automatically)
Using Homebrew's brew tap-new command sets up the proper structure:
# Create the tap locally with GitHub Packages support
brew tap-new sirkirby/homebrew-ten-second-tom --github-packages
# This creates:
# - Formula/ directory for formula files
# - .github/workflows/ for bottle-building automation (if desired)
# - README.md with tap documentation
# - Proper git configuration
# Push to GitHub
cd $(brew --repository sirkirby/homebrew-ten-second-tom)
gh repo create sirkirby/homebrew-ten-second-tom --public --source=. --pushNote: The --github-packages flag indicates support for GitHub Packages, though our workflow currently uploads bottles to GitHub Releases for simplicity.
If you prefer manual setup or already created the repository:
# Clone your tap repository
git clone https://github.com/sirkirby/homebrew-ten-second-tom.git
cd homebrew-ten-second-tom
# Create Formula directory
mkdir -p Formula
touch Formula/.gitkeep
# Create README
cat > README.md <<'EOF'
# Ten Second Tom Homebrew Tap
Official Homebrew tap for [Ten Second Tom](https://github.com/sirkirby/ten-second-tom).
## Installation
```bash
brew tap sirkirby/ten-second-tom
brew install ten-second-tomtom --helpEOF
git add . git commit -m "Initialize Homebrew tap" git push origin main
### 4. Create Initial Formula (Optional)
The release workflow generates the formula automatically, but you can create an initial placeholder:
Create `Formula/ten-second-tom.rb`:
```ruby
class TenSecondTom < Formula
desc "CLI tool for daily work summaries using Claude AI"
homepage "https://github.com/sirkirby/ten-second-tom"
url "https://github.com/sirkirby/ten-second-tom/archive/refs/tags/v0.1.0.tar.gz"
version "0.1.0"
license "MIT"
# Bottles provide fast installation without building from source
bottle do
root_url "https://github.com/sirkirby/ten-second-tom/releases/download/v0.1.0"
sha256 cellar: :any_skip_relocation, arm64_monterey: "PLACEHOLDER"
sha256 cellar: :any_skip_relocation, monterey: "PLACEHOLDER"
end
def install
bin.install "tom"
end
test do
system "#{bin}/tom", "--version"
end
end
Important Notes:
- The
bottle doblock enables fast binary installation for users - The release workflow automatically generates this with correct checksums
cellar: :any_skip_relocationmeans the binary is self-contained (no dependencies)- Separate bottles for Intel (monterey) and Apple Silicon (arm64_monterey)
Create a personal access token with permissions to update the tap repository:
- Go to GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click "Generate new token"
- Configure:
- Token name:
Homebrew Tap Token for Ten Second Tom - Expiration: 1 year (recommended) or custom
- Repository access: Only select repositories
- Select:
sirkirby/ten-second-tom-homebrew(your tap repository)
- Token name:
- Set Repository permissions:
- Contents: Read and write (required to push formula updates)
- Metadata: Read-only (automatically included)
- Click "Generate token"
- Copy the token immediately (it won't be shown again)
- Go to main repository:
https://github.com/sirkirby/ten-second-tom - Navigate to Settings → Secrets and variables → Actions
- Click "New repository secret"
- Set:
- Name:
HOMEBREW_TAP_TOKEN - Secret: Paste the token you generated
- Name:
- Click "Add secret"
Important: Also add this secret to the production environment:
- Go to Settings → Environments → production
- Scroll to "Environment secrets"
- Click "Add secret"
- Add
HOMEBREW_TAP_TOKENwith the same value
Before releasing, test the tap installation locally:
# Tap your repository
brew tap sirkirby/ten-second-tom
# Install the formula
brew install ten-second-tom
# Test the installation
tom --version
# Cleanup
brew uninstall ten-second-tom
brew untap sirkirby/ten-second-tomWhen users install your formula:
With Bottles (after release workflow):
$ brew install sirkirby/ten-second-tom/ten-second-tom
==> Downloading https://github.com/sirkirby/ten-second-tom/releases/download/v1.0.0/ten-second-tom--1.0.0.arm64_monterey.bottle.tar.gz
==> Pouring ten-second-tom--1.0.0.arm64_monterey.bottle.tar.gz
🍺 /opt/homebrew/Cellar/ten-second-tom/1.0.0: 1 file, 8.5MBBenefits:
- Fast installation (seconds instead of minutes)
- No build tools required
- Consistent across all machines
- Architecture-optimized (ARM64 or Intel x64)
How It Works:
- Release workflow creates bottle tarball from pre-built binary
- Bottle uploaded to GitHub releases alongside source code
- Formula updated with bottle SHA256 checksums
- Homebrew downloads bottle instead of building from source
- User gets instant installation
- Navigate to: Settings → Secrets and variables → Actions
- Click "New repository secret"
- Configure:
- Name:
HOMEBREW_TAP_TOKEN - Secret: Paste the token from previous step
- Name:
- Click "Add secret"
The release workflow uses a GitHub Environment to require manual approval before publishing to Homebrew:
- Go to repository Settings → Environments
- Click "New environment"
- Name:
production(exact name required) - Click "Configure environment"
- Configure settings:
- ✅ Required reviewers: Add
@sirkirby(or your maintainer team) - ✅ Deployment branches: Select "Selected branches"
- Add rule:
refs/tags/v*(allow deployments from version tags)
- Add rule:
- ✅ Required reviewers: Add
- Click "Save protection rules"
After the first release is published (or using a manual test release):
# Add your tap
brew tap sirkirby/ten-second-tom
# Verify tap was added
brew tap | grep ten-second-tom
# Install from tap
brew install sirkirby/ten-second-tom/ten-second-tom
# Verify installation
ten-second-tom --version
# Update to latest version
brew upgrade ten-second-tom
# Uninstall (cleanup)
brew uninstall ten-second-tom
brew untap sirkirby/ten-second-tomThe release workflow generates formulas with the following structure:
class TenSecondTom < Formula
desc "CLI tool for daily work summaries using Claude AI"
homepage "https://github.com/sirkirby/ten-second-tom"
version "1.2.3" # Extracted from release tag
license "MIT"
# Architecture-specific downloads
if Hardware::CPU.intel?
url "https://github.com/sirkirby/ten-second-tom/releases/download/v1.2.3/tom"
sha256 "abc123..." # macOS x64 SHA256 checksum
else
url "https://github.com/sirkirby/ten-second-tom/releases/download/v1.2.3/tom"
sha256 "def456..." # macOS ARM64 SHA256 checksum
end
def install
bin.install "tom"
end
test do
system "#{bin}/tom", "--version"
end
endProblem: Token authentication failure
Solution:
- Verify
HOMEBREW_TAP_TOKENsecret exists in repository - Check token hasn't expired (regenerate if needed)
- Verify token has
Contents: Read and writepermission on tap repository - Test token manually:
curl -H "Authorization: token YOUR_TOKEN" \ https://api.github.com/repos/sirkirby/homebrew-ten-second-tom
Problem: Formula syntax errors
Solution:
- Install Homebrew locally
- Clone tap repository
- Test formula syntax:
brew audit --strict Formula/ten-second-tom.rb brew style Formula/ten-second-tom.rb
- Test installation:
brew install --build-from-source Formula/ten-second-tom.rb
Problem: Approval not requested for production environment
Solution:
- Verify "production" environment exists in repository settings
- Check required reviewers are configured
- Verify deployment branches include
refs/tags/v* - Check
.github/CODEOWNERSincludes workflow maintainers
Problem: Wrong architecture downloaded
Solution:
- Verify formula uses
Hardware::CPU.intel?check - Verify both x64 and ARM64 URLs point to correct binaries
- Test on both Intel and Apple Silicon Macs if possible
- Version numbers: Always use semantic versioning (MAJOR.MINOR.PATCH)
- Checksums: Must be SHA256 checksums of downloaded binaries
- Binary names: Keep binary name consistent across releases
- Test block: Always include a simple test (like
--version) - Description: Keep concise but descriptive
- License: Match the main repository license
If automated publication fails, you can manually update the formula:
# Clone tap repository
git clone https://github.com/sirkirby/ten-second-tom-homebrew.git
cd ten-second-tom-homebrew
# Edit formula
vim Formula/ten-second-tom.rb
# Update version, URLs, and checksums
# Get checksums from GitHub release assets
# Test locally
brew audit --strict Formula/ten-second-tom.rb
brew install --build-from-source Formula/ten-second-tom.rb
# Commit and push
git add Formula/ten-second-tom.rb
git commit -m "Update to version X.Y.Z"
git push origin main
# Users can now update
brew update
brew upgrade ten-second-tomAdd these badges to README.md to show workflow status:
[](https://github.com/sirkirby/ten-second-tom/actions/workflows/pr-validation.yml)
[](https://github.com/sirkirby/ten-second-tom/actions/workflows/release.yml)Problem: Compiler warnings causing build failure
Solution:
- Check job logs for warning messages
- Fix warnings in source code
- Run
dotnet build --no-incremental --warnaserrorlocally to reproduce - Push fix to branch
Problem: NuGet restore failures
Solution:
- Check network connectivity in job logs
- Verify package sources in
NuGet.config - Clear cache by triggering workflow with
[clear cache]in commit message - Check for package deprecation warnings
Problem: Tests passing locally but failing in CI
Solution:
- Check for timing-dependent tests
- Check for file system path dependencies
- Check for environment variable dependencies
- Run tests in Release configuration locally:
dotnet test -c Release - Review test logs in artifact download
Problem: Test timeout
Solution:
- Identify slow tests in logs
- Optimize test performance
- Increase timeout in workflow if justified (current: 5 minutes)
Problem: Coverage below 80% threshold
Solution:
- Download coverage report artifact from job
- Open
index.htmlto see uncovered code - Add tests for uncovered lines
- Target: ≥80% line coverage
Problem: Coverage diff showing incorrect change
Solution:
- Check if baseline coverage was cached correctly
- Verify target branch (main) has recent coverage data
- Re-run workflow to refresh cache
Problem: Tag format rejected ("Version must start with 'v'")
Solution:
# Check current tag format
git describe --tags --exact-match
# If tag doesn't start with 'v', delete and recreate:
git tag -d 1.0.0
git push origin :refs/tags/1.0.0
git tag v1.0.0
git push origin v1.0.0Problem: Tag not on main branch
Solution:
The release workflow validates that tags are on commits that exist on the main branch (for production releases). This ensures all releases go through standard quality gates.
# Check if your current commit is on main
git branch -r --contains HEAD
# If not on main, merge your branch first:
git checkout main
git pull origin main
git merge your-feature-branch
git push origin main
# Then tag the merge commit:
git tag v1.0.0
git push origin v1.0.0For testing on PR branches, use workflow_dispatch with dry_run: true to skip the main branch check.
Problem: No build artifacts found for commit
Solution:
The release workflow builds artifacts as part of the release process (not reusing from a separate build workflow). If build fails:
# Check the release workflow logs for build errors
gh run list --workflow=release.yml
# View specific run details
gh run view <run-id>
# Common build issues:
# - Missing dependencies
# - .NET SDK version mismatch
# - Platform-specific build errorsTypical workflow:
- Create PR → triggers pr-validation.yml (build, test, coverage)
- Merge to main
- Tag merge commit → triggers release.yml (builds + publishes)
Problem: Invalid semantic version format
Solution:
- Verify format is exactly
vMAJOR.MINOR.PATCH(e.g.,v1.2.3) - No pre-release identifiers allowed (e.g.,
v1.0.0-betais invalid) - No build metadata allowed (e.g.,
v1.0.0+build123is invalid) - Examples:
- ✅ Valid:
v1.0.0,v2.3.4,v10.20.30 - ❌ Invalid:
v1.0,v1.0.0-alpha,v1.0.0+123,1.0.0(missing 'v')
- ✅ Valid:
Problem: Duplicate version error ("Version v1.0.0 already exists")
Solution:
- Check existing releases:
gh release list - If version should be updated:
# Delete the release gh release delete v1.0.0 -y # Delete the tag locally and remotely git tag -d v1.0.0 git push origin :refs/tags/v1.0.0 # Create new tag and push git tag v1.0.0 git push origin v1.0.0
- If version should be incremented:
# Create next version git tag v1.0.1 git push origin v1.0.1
Problem: Build fails for specific platform
Solution:
- Check job logs for compilation errors
- Test build locally:
# macOS x64 dotnet publish src/TenSecondTom.csproj \ -c Release \ -r osx-x64 \ --self-contained \ -p:PublishSingleFile=true \ -p:IncludeNativeLibrariesForSelfExtract=true # macOS ARM64 dotnet publish src/TenSecondTom.csproj \ -c Release \ -r osx-arm64 \ --self-contained \ -p:PublishSingleFile=true # Windows x64 dotnet publish src/TenSecondTom.csproj \ -c Release \ -r win-x64 \ --self-contained \ -p:PublishSingleFile=true
- Fix compilation errors and push fix
- Delete and recreate tag to retrigger workflow
Problem: Smoke test fails ("Command '--version' failed")
Solution:
- Download artifact from failed workflow
- Test executable locally:
# macOS chmod +x ./tom ./tom --version # Windows .\tom.exe --version
- Check for missing dependencies:
- macOS: Verify
libsodium.dylibis included - Windows: Verify required DLLs are included
- macOS: Verify
- Check
appsettings.jsonis present and valid - Review application startup logs for initialization errors
Problem: Checksum calculation fails
Solution:
- Verify executable file exists in publish output
- Check file permissions allow reading
- Verify
shasum(macOS) orcertutil(Windows) is available - Calculate checksum manually to verify:
# macOS/Linux shasum -a 256 TenSecondTom # Windows PowerShell Get-FileHash TenSecondTom.exe -Algorithm SHA256
Problem: Release creation fails ("Resource not accessible")
Solution:
- Verify repository permissions (workflow has write access by default)
- Check if release with same tag exists:
gh release list - Delete existing release if needed:
gh release delete v1.0.0 - Verify artifact downloads succeeded in previous job
- Check GitHub API status: https://www.githubstatus.com/
Problem: Asset upload fails
Solution:
- Check artifact file size (GitHub limit: 2GB per file)
- Verify artifact download completed successfully
- Check file exists in workflow directory:
- name: List downloaded artifacts run: ls -lah release-*/
- Retry upload or use
gh release uploadwith--clobberflag
Problem: Release notes generation fails
Solution:
- Verify git history exists between tags
- Check previous tag exists:
git tag | sort -V - Generate release notes manually:
# Get commits since last tag git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --oneline # Use gh CLI gh release create v1.0.0 --generate-notes
Problem: Authentication failure ("Resource not accessible by personal access token")
Solution:
- Verify
HOMEBREW_TAP_TOKENsecret exists:- Go to repository Settings → Secrets and variables → Actions
- Confirm
HOMEBREW_TAP_TOKENis listed
- Check token hasn't expired:
- Go to GitHub Settings → Developer settings → Personal access tokens
- Check expiration date, regenerate if expired
- Verify token permissions:
- Required:
Contents: Read and writeon tap repository - Test token manually:
curl -H "Authorization: token YOUR_TOKEN" \ https://api.github.com/repos/sirkirby/homebrew-ten-second-tom
- Required:
- Regenerate token if needed and update secret
Problem: Formula syntax errors ("brew audit failed")
Solution:
- Clone tap repository locally
- Test formula syntax:
brew audit --strict Formula/ten-second-tom.rb brew style Formula/ten-second-tom.rb
- Common issues:
- Invalid Ruby syntax (check brackets, quotes, indentation)
- Incorrect SHA256 format (must be 64 hex characters)
- Invalid URL format
- Missing required fields (desc, homepage, url, sha256)
- Fix formula in tap repository manually if needed
- Update workflow formula template if issue is in generation logic
Problem: Approval not requested (deployment doesn't wait)
Solution:
- Verify GitHub Environment exists:
- Go to repository Settings → Environments
- Confirm "production" environment exists
- Check environment configuration:
- Required reviewers: Must include at least one reviewer
- Deployment branches: Must include
refs/tags/v*pattern
- Verify CODEOWNERS configuration:
cat .github/CODEOWNERS # Should include: /.github/workflows/release.yml @sirkirby - Check job environment declaration:
publish-homebrew: environment: production # Must match environment name exactly
Problem: Formula installation fails for users
Solution:
- Test installation locally:
brew tap sirkirby/ten-second-tom brew install --verbose sirkirby/ten-second-tom/ten-second-tom
- Check formula downloads correct binaries:
- Intel Macs should download
osx-x64binary - Apple Silicon Macs should download
osx-arm64binary
- Intel Macs should download
- Verify checksums match:
# Download binary manually curl -L -o TenSecondTom https://github.com/sirkirby/ten-second-tom/releases/download/v1.0.0/TenSecondTom-osx-x64 shasum -a 256 TenSecondTom # Compare with SHA256 in formula
- Test binary works:
chmod +x TenSecondTom ./TenSecondTom --version
Problem: Tap repository push fails ("Updates were rejected")
Solution:
- Check for concurrent releases (concurrency group should prevent this)
- Verify tap repository hasn't been force-pushed
- Try pulling latest changes before pushing:
- name: Pull latest changes run: | cd homebrew-ten-second-tom git pull --rebase origin main git push origin main
Problem: Issue creation fails for Winget/Chocolatey
Solution:
- Verify workflow has
issues: writepermission - Check GitHub API rate limits: https://api.github.com/rate_limit
- Create issue manually with generated manifest content
- Verify issue template renders correctly (check Markdown syntax)
Problem: Generated manifests are invalid
Solution:
- Validate Winget manifest:
# Install winget-create tool winget install Microsoft.WingetCreate # Validate manifest wingetcreate validate --manifest .winget/manifests/
- Validate Chocolatey package:
# Install Chocolatey Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) # Test package choco pack .chocolatey/ten-second-tom.nuspec choco install ten-second-tom -source . -y
Problem: Multiple releases running simultaneously
Solution:
- Verify concurrency configuration in workflow:
concurrency: group: release-${{ github.ref }} cancel-in-progress: false
- Check workflow runs for same tag:
gh run list --workflow=release.yml --branch=v1.0.0
- Cancel duplicate runs manually:
gh run cancel <run-id>
- Wait for first run to complete before retriggering
Problem: Release stuck "Waiting for approval"
Solution:
- Check who can approve:
- Go to workflow run page
- Click "Review deployments" button
- Verify your GitHub account is listed as required reviewer
- If not listed, update environment configuration:
- Settings → Environments → production → Required reviewers
- Approve deployment:
- Click "Review deployments" button
- Select "production" environment
- Click "Approve and deploy"
Problem: Performance target exceeded (>10 minutes excluding approval)
Solution: The release workflow should complete in ~10 minutes by reusing build artifacts. If it's slower:
-
Check artifact download time:
- Look at "Download Build Artifacts" job timing
- Large artifacts or slow network can cause delays
- Artifacts are typically 30-50MB total
-
Check GitHub Release creation time:
- Look at "Create GitHub Release" job timing
- Release note generation should be <30 seconds
- Asset upload should be <1 minute
-
Check Homebrew publication time:
- Look at "Publish to Homebrew" job timing (excluding approval wait)
- Git clone/push should be <1 minute
- If slow, check tap repository size
-
Verify no builds are happening:
- Release workflow should NOT rebuild executables
- If you see "dotnet publish" commands, the refactor didn't work
- All executables should come from pre-built artifacts
Expected job timings:
- validate-version: 1-2 minutes
- download-artifacts: 30-60 seconds
- create-github-release: 1-2 minutes
- publish-homebrew: 2-5 minutes (excluding approval)
- document-*: 10-30 seconds each
If builds are happening (wrong!):
- Check that the workflow is actually downloading artifacts
- Verify
needs: validate-versionoutputs are correct - Check GitHub Actions logs for "Found build workflow run" message
Solution:
- Identify slow job(s) in workflow timeline
- Common slow jobs:
- Build artifacts: Check if NuGet cache is being used
- Homebrew publication: May be waiting for approval
- Optimize slow steps:
# Improve cache hit rate - uses: actions/cache@v3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} restore-keys: | ${{ runner.os }}-nuget-
- Consider parallel execution where possible (already implemented for builds)
Problem: Build artifacts job timeout (>15 minutes)
Solution:
- Check if builds are running in parallel (matrix strategy)
- Verify NuGet cache is working:
- name: Check cache hit run: echo "Cache hit: ${{ steps.cache.outputs.cache-hit }}"
- Reduce dependencies or enable more aggressive trimming
- Check GitHub Actions runner performance (may be throttled)
Problem: Winget/Chocolatey documentation jobs fail
Solution:
- Verify workflow has
issues: writepermission - Check GitHub API rate limits:
https://api.github.com/rate_limit - Create issue manually with generated manifest content if needed
- Verify issue template renders correctly (check Markdown syntax)
For issues with CI/CD workflows:
- Check this troubleshooting guide first
- Review workflow logs in Actions tab
- Check existing Issues for similar problems
- Create new Issue with:
- Workflow name and run number
- Error messages from logs
- Steps to reproduce (if applicable)
- Relevant workflow file snippets
Maintainer: @sirkirby
Documentation: This file (docs/CICD.md)
Workflow Files: .github/workflows/
macOS:
- Apple Developer Program membership available
- Implement code signing via
codesigncommand - Implement notarization via
notarytool - Benefits: Eliminates Gatekeeper warnings entirely
Windows:
- Code signing certificate required ($100-400/year)
- Implement signing via
signtoolcommand - Benefits: Improves SmartScreen reputation
Status: Deferred per research.md; unsigned binaries work for initial release
- Automate manifest generation (currently manual)
- Automate PR creation to microsoft/winget-pkgs
- Configure
WINGET_TOKENfor authentication
- Automate package creation and publication
- Configure
CHOCOLATEY_API_KEY - Test package installation on Windows
Document Version: 2.0.0
Last Updated: 2025-10-17