CI/CD Pipeline & Release Process

This document describes the automated CI/CD pipeline and release process for portctl.

Overview

The portctl project uses Dagger for all CI/CD automation, providing a modular, reproducible, and platform-agnostic pipeline. All build, test, and release steps are defined as Dagger functions in .dagger/main.go.

Pipeline Architecture

Dagger Functions

All pipeline steps are callable via dagger call <function>:

# Core development workflow
dagger call lint --src=.                    # Run golangci-lint
dagger call test --src=.                    # Run Go tests
dagger call build --src=.                   # Build binary

# Release workflow
dagger call generate-manifest --src=.       # Generate MCP manifest
dagger call release --src=. export --path=./artifacts  # Release with GoReleaser
dagger call publish-image --src=.           # Publish Docker images

# Quality & Security
dagger call security-scan --src=.           # Run gosec security scan
dagger call sbom --src=.                    # Generate SBOM
dagger call snapshot-test --src=.           # Run snapshot tests
dagger call well-known --src=.              # Validate .well-known metadata

# Documentation
dagger call docs --src=.                    # Build mdBook documentation
dagger call publish-docs --src=.            # Publish to GitHub Pages

GitHub Actions Workflows

CI Workflow (.github/workflows/ci.yml)

Runs on every push and pull request:

  • Linting with golangci-lint
  • Unit tests with coverage
  • Security scanning with gosec
  • SBOM generation
  • Documentation build

Release Workflow (.github/workflows/release.yml)

Triggered on version tags (e.g., v1.0.1):

  1. Validation: Runs lint and test steps
  2. Build & Release:
    • Runs GoReleaser to build multi-platform binaries
    • Generates SBOM and SLSA attestations
    • Creates GitHub release with artifacts
    • Updates Homebrew tap
  3. Artifact Export: Exports artifacts to host filesystem for upload
  4. Docker Publishing: Builds and publishes multi-arch images to GHCR
  5. Artifact Upload: Uploads artifacts to GitHub Actions

Release Process

Creating a Release

  1. Ensure all changes are committed and pushed:

    git add .
    git commit -m "feat: your changes"
    git push origin main
    
  2. Create and push a version tag:

    # For a patch release (bug fixes)
    git tag -a v1.0.1 -m "Release v1.0.1"
    
    # For a minor release (new features)
    git tag -a v1.1.0 -m "Release v1.1.0"
    
    # For a major release (breaking changes)
    git tag -a v2.0.0 -m "Release v2.0.0"
    
    git push origin <tag-name>
    
  3. Monitor the release workflow:

    • Go to https://github.com/ckodex-labs/portctl/actions
    • Watch the "Release" workflow run
    • Verify all steps complete successfully

What Gets Released

Each release includes:

  • Multi-platform binaries: Linux, macOS, Windows (amd64, arm64)
  • Docker images: Published to ghcr.io/ckodex-labs/portctl with tags:
    • latest - Always points to the latest release
    • v1.0.x - Specific version tag
  • Security & Compliance Artifacts:
    • *.sbom.spdx.json - Software Bill of Materials in SPDX format
    • *.sbom.cyclonedx.json - Software Bill of Materials in CycloneDX format
    • *.intoto.jsonl - SLSA provenance attestations (in-toto format)
  • AI Integration Metadata (.well-known/ folder):
    • mcp-manifest.jsonld - Model Context Protocol server manifest
    • llms.txt - LLM guidance and context for AI agents
    • skills.txt - Capability descriptions for MCP tools
  • Homebrew formula: Automatically updated in ckodex-labs/homebrew-tap

Artifact Publishing

The release workflow exports artifacts from Dagger containers to the GitHub Actions runner:

- name: Build and Release
  run: dagger call release --src=. --github-token=env:GITHUB_TOKEN --tap-github-token=env:TAP_GITHUB_TOKEN export --path=./artifacts

- name: Upload Artifacts
  uses: actions/upload-artifact@v4
  with:
    name: release-artifacts
    path: ./artifacts

The export --path=./artifacts command writes the Dagger directory to the host filesystem, making artifacts available for upload.

Local Testing

Test the release process locally before pushing tags:

# Set required environment variables
export GITHUB_TOKEN="your-github-token"
export TAP_GITHUB_TOKEN="your-tap-token"

# Test the release build (without publishing)
dagger call release --src=. --github-token=env:GITHUB_TOKEN --tap-github-token=env:TAP_GITHUB_TOKEN export --path=./artifacts

# Verify artifacts were created
ls -la ./artifacts

Security & Compliance

SLSA Provenance

All releases include SLSA (Supply chain Levels for Software Artifacts) provenance attestations:

  • Build provenance is generated by GoReleaser
  • Attestations are signed and published with releases
  • Verifiable supply chain security

SBOM (Software Bill of Materials)

Each release includes a comprehensive SBOM:

  • Generated using Syft
  • Lists all dependencies and their versions
  • Helps with vulnerability tracking and compliance

SLSA Level 3 Compliance

portctl achieves SLSA Level 3 through:

  • Signed provenance: GoReleaser generates signed .intoto.jsonl attestations
  • Cosign signatures: All artifacts are cryptographically signed with keyless signing
  • SBOM generation: Comprehensive dependency tracking with Syft
  • Hardened build: GitHub Actions with OIDC authentication

Verifying Signatures

Download and verify a release artifact:

# Download artifacts
wget https://github.com/ckodex-labs/portctl/releases/download/v1.0.2/portctl_linux_amd64.tar.gz
wget https://github.com/ckodex-labs/portctl/releases/download/v1.0.2/portctl_linux_amd64.tar.gz.sig
wget https://github.com/ckodex-labs/portctl/releases/download/v1.0.2/portctl_linux_amd64.tar.gz.cert

# Verify with Cosign
cosign verify-blob \
  --certificate portctl_linux_amd64.tar.gz.cert \
  --signature portctl_linux_amd64.tar.gz.sig \
  --certificate-identity-regexp="https://github.com/ckodex-labs/portctl" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  portctl_linux_amd64.tar.gz

Verifying SLSA Provenance

# Download provenance
wget https://github.com/ckodex-labs/portctl/releases/download/v1.0.2/portctl_checksums.txt.intoto.jsonl

# Inspect provenance
cat portctl_checksums.txt.intoto.jsonl | jq

Documentation Publishing

Documentation is built with mdBook and published to GitHub Pages:

# Build documentation locally
dagger call docs --src=.

# Publish to GitHub Pages (requires GITHUB_TOKEN)
dagger call publish-docs --src=.

The documentation is automatically published on every release.

Troubleshooting

Release Workflow Fails

  1. Check the GitHub Actions logs for specific errors
  2. Verify all required secrets are set:
    • GITHUB_TOKEN (automatically provided)
    • TAP_GITHUB_TOKEN (for Homebrew tap updates)
  3. Ensure the tag follows semantic versioning (e.g., v1.0.1)

Artifacts Not Uploaded

The artifact export mechanism requires:

  • Dagger Release function returns *dagger.Directory
  • Workflow uses export --path=./artifacts
  • Upload step references the correct path

Docker Publishing Fails

Verify:

  • GitHub Container Registry permissions are configured
  • GITHUB_TOKEN has packages: write permission
  • Multi-arch build platforms are supported

Best Practices

  1. Test locally first: Always test builds locally before creating tags
  2. Follow semantic versioning: Use appropriate version numbers
  3. Write clear release notes: Document changes in tag messages
  4. Monitor workflows: Watch GitHub Actions to catch issues early
  5. Verify artifacts: Download and test released artifacts