Skip to content

Fix SBOM action permissions issue #18

Fix SBOM action permissions issue

Fix SBOM action permissions issue #18

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*.*.*'
- '*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to create release for (e.g., v0.0.13)'
required: true
type: string
env:
REGISTRY: docker.io
IMAGE_NAME: gdxbsv/traktor
jobs:
validate-tag:
name: Validate Release Tag
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
- name: Validate tag format
id: validate_tag
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
if [[ ! "$TAG_NAME" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then
echo "Invalid tag format: $TAG_NAME"
echo "Expected format: v1.2.3, 1.2.3, v1.2.3-alpha, or 1.2.3-alpha"
exit 1
fi
echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
- name: Get version
id: get_version
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
# Remove 'v' prefix if present
VERSION="${TAG_NAME#v}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Check if prerelease
id: check_prerelease
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
if [[ "$TAG_NAME" =~ (alpha|beta|rc) ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
fi
test:
name: Run Tests
runs-on: ubuntu-latest
needs: validate-tag
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Run unit tests
run: make test
- name: Run linter
uses: golangci/golangci-lint-action@v8
with:
version: v2.1.0
args: --timeout=5m
build-multi-arch:
name: Build Multi-Architecture Images
runs-on: ubuntu-latest
needs: test
timeout-minutes: 30
permissions:
contents: write
packages: write
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}},enable=${{ !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') }}
type=raw,value=latest,enable=${{ !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') }}
- name: Debug metadata
run: |
echo "Generated tags:"
echo "${{ steps.meta.outputs.tags }}"
echo ""
echo "Version: ${{ steps.meta.outputs.version }}"
echo "Digest will be available after build"
- name: Build and push multi-arch image
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ needs.validate-tag.outputs.version }}
COMMIT_SHA=${{ github.sha }}
# Enable build cache mounts for faster Go compilation
provenance: false
sbom: false
- name: Debug build output
run: |
echo "Build digest: ${{ steps.build.outputs.digest }}"
echo "Image reference: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}"
# Generate SBOM for the Docker image
# Note: We don't upload to release here because it doesn't exist yet.
# The SBOM file will be manually attached to the release in the create-release job.
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
format: spdx-json
output-file: sbom-${{ needs.validate-tag.outputs.version }}.spdx.json
upload-artifact: false
upload-release-assets: false
- name: Verify SBOM was created
run: |
if [ -f "sbom-${{ needs.validate-tag.outputs.version }}.spdx.json" ]; then
echo "✅ SBOM file created successfully"
ls -lh sbom-${{ needs.validate-tag.outputs.version }}.spdx.json
else
echo "❌ SBOM file not found"
exit 1
fi
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom-${{ needs.validate-tag.outputs.version }}.spdx.json
generate-manifests:
name: Generate Release Manifests
runs-on: ubuntu-latest
needs: [validate-tag, build-multi-arch]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Generate CRDs
run: make manifests
- name: Generate install manifest
run: |
IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.validate-tag.outputs.version }}
make build-installer IMG=$IMG
- name: Verify manifest
run: |
if [ ! -f dist/install.yaml ]; then
echo "install.yaml not generated"
exit 1
fi
echo "Manifest size: $(wc -l < dist/install.yaml) lines"
- name: Upload manifest artifact
uses: actions/upload-artifact@v4
with:
name: install-manifest
path: dist/install.yaml
- name: Generate kustomize examples
run: |
mkdir -p release-artifacts
cp dist/install.yaml release-artifacts/install.yaml
cp -r config/samples release-artifacts/
cd release-artifacts && tar -czf traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz install.yaml samples/
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: release-artifacts
path: release-artifacts/traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz
package-helm-chart:
name: Package Helm Chart
runs-on: ubuntu-latest
needs: [validate-tag, build-multi-arch]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: 'v3.13.0'
- name: Update Chart version
run: |
VERSION=${{ needs.validate-tag.outputs.version }}
sed -i "s/^version:.*/version: ${VERSION}/" charts/traktor/Chart.yaml
sed -i "s/^appVersion:.*/appVersion: \"${VERSION}\"/" charts/traktor/Chart.yaml
sed -i "s|url: https://github.com/GDXbsv/traktor/releases/tag/v[0-9]*\.[0-9]*\.[0-9]*|url: https://github.com/GDXbsv/traktor/releases/tag/v${VERSION}|g" charts/traktor/Chart.yaml
- name: Update Chart image tag
run: |
VERSION=${{ needs.validate-tag.outputs.version }}
sed -i "s/tag: \"\"/tag: \"${VERSION}\"/" charts/traktor/values.yaml
- name: Lint Helm chart
run: |
helm lint charts/traktor
- name: Package Helm chart
run: |
mkdir -p release-artifacts
helm package charts/traktor -d release-artifacts
- name: Generate Helm chart index
run: |
cd release-artifacts
helm repo index . --url https://github.com/GDXbsv/traktor/releases/download/${{ github.ref_name }}
- name: Upload Helm chart artifact
uses: actions/upload-artifact@v4
with:
name: helm-chart
path: |
release-artifacts/*.tgz
release-artifacts/index.yaml
security-scan:
name: Security Vulnerability Scan
runs-on: ubuntu-latest
needs: [validate-tag, build-multi-arch]
permissions:
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-multi-arch.outputs.digest }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Run Trivy in table mode
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-multi-arch.outputs.digest }}
format: 'table'
severity: 'CRITICAL,HIGH,MEDIUM'
- name: Fail on critical vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-multi-arch.outputs.digest }}
format: 'table'
exit-code: '1'
severity: 'CRITICAL'
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [validate-tag, build-multi-arch, generate-manifests, package-helm-chart, security-scan]
permissions:
contents: write
pull-requests: read
repository-projects: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download manifests
uses: actions/download-artifact@v4
with:
name: install-manifest
path: ./artifacts/
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
name: release-artifacts
path: ./artifacts/
- name: Download SBOM
uses: actions/download-artifact@v4
with:
name: sbom
path: ./artifacts/
- name: Download Helm chart
uses: actions/download-artifact@v4
with:
name: helm-chart
path: ./artifacts/helm/
- name: Check if tag and release exist
id: check_release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
# Check if tag exists
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
echo "tag_exists=true" >> $GITHUB_OUTPUT
echo "✓ Tag $TAG_NAME exists"
else
echo "tag_exists=false" >> $GITHUB_OUTPUT
echo "✗ Tag $TAG_NAME does not exist"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "Error: Tag $TAG_NAME must exist before creating a release"
exit 1
fi
fi
# Check if release exists
if gh release view $TAG_NAME --repo ${{ github.repository }} >/dev/null 2>&1; then
echo "release_exists=true" >> $GITHUB_OUTPUT
echo "✓ Release $TAG_NAME already exists"
else
echo "release_exists=false" >> $GITHUB_OUTPUT
echo "✗ Release $TAG_NAME does not exist (will be created)"
fi
- name: Generate changelog
id: changelog
if: steps.check_release.outputs.release_exists == 'false'
run: |
# Get tag name
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
# Get previous tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
echo "First release"
CHANGELOG="Initial release of Traktor Operator"
else
echo "Generating changelog from $PREV_TAG to $TAG_NAME"
CHANGELOG=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s (%an)" --no-merges)
fi
# Save to file
cat > CHANGELOG.md << EOF
## What's Changed
${CHANGELOG}
## Docker Images
\`\`\`bash
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.validate-tag.outputs.version }}
\`\`\`
## Installation
### Using kubectl
\`\`\`bash
kubectl apply -f https://github.com/GDXbsv/traktor/releases/download/$TAG_NAME/install.yaml
\`\`\`
### Using Helm
\`\`\`bash
helm install traktor https://github.com/GDXbsv/traktor/releases/download/$TAG_NAME/traktor-${{ needs.validate-tag.outputs.version }}.tgz
\`\`\`
## Supported Architectures
- linux/amd64
- linux/arm64
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...$TAG_NAME
EOF
- name: Create Release with GitHub CLI
if: steps.check_release.outputs.release_exists == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
echo "========================================="
echo "Creating GitHub Release for tag: $TAG_NAME"
echo "Tag exists: ${{ steps.check_release.outputs.tag_exists }}"
echo "Release exists: ${{ steps.check_release.outputs.release_exists }}"
echo "========================================="
# Determine if prerelease
if [ "${{ needs.validate-tag.outputs.is_prerelease }}" = "true" ]; then
PRERELEASE_FLAG="--prerelease"
else
PRERELEASE_FLAG=""
fi
# List available artifacts for debugging
echo "Available artifacts:"
find artifacts -type f
# Build file list, checking each file exists
FILES=""
# Required files
if [ -f "artifacts/install.yaml" ]; then
FILES="$FILES artifacts/install.yaml"
else
echo "Warning: install.yaml not found"
fi
if [ -f "artifacts/traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz" ]; then
FILES="$FILES artifacts/traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz"
else
echo "Warning: manifests tarball not found"
fi
if [ -f "artifacts/sbom-${{ needs.validate-tag.outputs.version }}.spdx.json" ]; then
FILES="$FILES artifacts/sbom-${{ needs.validate-tag.outputs.version }}.spdx.json"
else
echo "Warning: SBOM file not found"
fi
# Helm files
if [ -f "artifacts/helm/index.yaml" ]; then
FILES="$FILES artifacts/helm/index.yaml"
else
echo "Warning: Helm index.yaml not found"
fi
# Add all helm chart tgz files
for chart in artifacts/helm/*.tgz; do
if [ -f "$chart" ]; then
FILES="$FILES $chart"
fi
done
# Create release with available files
if [ -z "$FILES" ]; then
echo "Error: No artifacts found to upload"
exit 1
fi
echo "Creating release $TAG_NAME with files: $FILES"
# Create release on existing tag
gh release create $TAG_NAME \
--repo ${{ github.repository }} \
--title "Release ${{ needs.validate-tag.outputs.version }}" \
--notes-file CHANGELOG.md \
$PRERELEASE_FLAG \
$FILES
echo "✓ Release $TAG_NAME created successfully"
- name: Upload assets to existing release
if: steps.check_release.outputs.release_exists == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME="${{ github.event.inputs.tag || github.ref_name }}"
echo "========================================="
echo "Updating existing release: $TAG_NAME"
echo "========================================="
# List available artifacts for debugging
echo "Available artifacts:"
find artifacts -type f
# Build file list, checking each file exists
FILES=""
# Required files
if [ -f "artifacts/install.yaml" ]; then
FILES="$FILES artifacts/install.yaml"
else
echo "Warning: install.yaml not found"
fi
if [ -f "artifacts/traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz" ]; then
FILES="$FILES artifacts/traktor-${{ needs.validate-tag.outputs.version }}-manifests.tar.gz"
else
echo "Warning: manifests tarball not found"
fi
if [ -f "artifacts/sbom-${{ needs.validate-tag.outputs.version }}.spdx.json" ]; then
FILES="$FILES artifacts/sbom-${{ needs.validate-tag.outputs.version }}.spdx.json"
else
echo "Warning: SBOM file not found"
fi
# Helm files
if [ -f "artifacts/helm/index.yaml" ]; then
FILES="$FILES artifacts/helm/index.yaml"
else
echo "Warning: Helm index.yaml not found"
fi
# Add all helm chart tgz files
for chart in artifacts/helm/*.tgz; do
if [ -f "$chart" ]; then
FILES="$FILES $chart"
fi
done
# Upload files to existing release
if [ -z "$FILES" ]; then
echo "Error: No artifacts found to upload"
exit 1
fi
echo "Uploading files to existing release $TAG_NAME: $FILES"
gh release upload $TAG_NAME \
--repo ${{ github.repository }} \
--clobber \
$FILES
echo "✓ Assets uploaded successfully to release $TAG_NAME"
update-docs:
name: Update Documentation
runs-on: ubuntu-latest
needs: [validate-tag, create-release]
if: needs.validate-tag.outputs.is_prerelease == 'false'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Update README version
run: |
VERSION=${{ needs.validate-tag.outputs.version }}
sed -i "s/traktor:v[0-9]*\.[0-9]*\.[0-9]*/traktor:v${VERSION}/g" README.md
sed -i "s/traktor:latest/traktor:v${VERSION}/g" README.md
- name: Commit and push changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md
if git diff --staged --quiet; then
echo "No changes to commit"
exit 0
fi
git commit -m "docs: update version to ${{ needs.validate-tag.outputs.version }} [skip ci]"
git push origin main
publish-helm-repo:
name: Publish Helm Repository to GitHub Pages
runs-on: ubuntu-latest
needs: [validate-tag, package-helm-chart, create-release]
permissions:
contents: write
pages: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: v3.13.0
- name: Checkout gh-pages branch
uses: actions/checkout@v4
with:
ref: gh-pages
path: gh-pages
- name: Download Helm chart artifact
uses: actions/download-artifact@v4
with:
name: helm-chart
path: ./helm-artifacts/
- name: Update Helm repository
run: |
# Remove old chart versions with invalid metadata
# This prevents Artifact Hub validation errors
rm -f gh-pages/traktor-0.0.1.tgz || true
rm -f gh-pages/traktor-0.0.2.tgz || true
rm -f gh-pages/traktor-0.0.4.tgz || true
rm -f gh-pages/traktor-0.0.5.tgz || true
# Copy new package to gh-pages
mkdir -p gh-pages
cp helm-artifacts/*.tgz gh-pages/ || true
# Generate/update index
helm repo index gh-pages --url https://gdxbsv.github.io/traktor
# Add Artifact Hub repository metadata
cat > gh-pages/artifacthub-repo.yml << 'EOF'
repositoryID: 2a1be652-53bf-42c6-a2b5-7b87f26bc585
displayName: Traktor Operator
owners:
- name: GDX Cloud
email: support@gdxcloud.net
EOF
- name: Deploy to GitHub Pages
run: |
cd gh-pages
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git add .
git commit -m "Release Helm chart ${{ needs.validate-tag.outputs.version }}" || echo "No changes to commit"
git push origin gh-pages
notify-success:
name: Notify Release Success
runs-on: ubuntu-latest
needs: [validate-tag, create-release, publish-helm-repo]
if: success()
steps:
- name: Release summary
run: |
echo "🎉 Release ${{ github.ref_name }} completed successfully!"
echo ""
echo "📦 Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.validate-tag.outputs.version }}"
echo "📄 Release Notes: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}"
echo "🗂️ Helm Repository: https://gdxbsv.github.io/traktor"
echo ""
echo "Installation with kubectl:"
echo "kubectl apply -f https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/install.yaml"
echo ""
echo "Installation with Helm:"
echo "helm repo add traktor https://gdxbsv.github.io/traktor"
echo "helm repo update"
echo "helm install traktor traktor/traktor --version ${{ needs.validate-tag.outputs.version }}"