Docker tags cannot contain colons. Use a separate tag-safe format (colons replaced with hyphens) for Docker tags while keeping the RFC 3339 timestamp as the version baked into the binary.
300 lines
9.7 KiB
YAML
300 lines
9.7 KiB
YAML
# GitHub Actions CI/CD Configuration for Towerops Agent
|
|
# Builds and publishes Docker image to GitHub Container Registry
|
|
|
|
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
- "**"
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository }}
|
|
DOCKERHUB_IMAGE: gmcintire/towerops-agent
|
|
|
|
jobs:
|
|
test:
|
|
name: Test
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version: "1.25"
|
|
|
|
- name: Vet
|
|
run: go vet ./...
|
|
|
|
- name: Lint
|
|
uses: golangci/golangci-lint-action@v9
|
|
with:
|
|
version: latest
|
|
|
|
- name: Test
|
|
run: go test -v ./...
|
|
|
|
- name: Build
|
|
run: CGO_ENABLED=0 go build -o /dev/null .
|
|
|
|
build-branch:
|
|
name: Build (Branch)
|
|
needs: [test]
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
if: github.event_name == 'push' && github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Blacksmith Builder
|
|
uses: useblacksmith/setup-docker-builder@v1
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract branch name
|
|
id: branch
|
|
run: |
|
|
BRANCH_NAME=${GITHUB_REF#refs/heads/}
|
|
# Sanitize branch name for Docker tag (replace / with -)
|
|
SAFE_BRANCH=$(echo "$BRANCH_NAME" | sed 's/\//-/g')
|
|
echo "name=$SAFE_BRANCH" >> $GITHUB_OUTPUT
|
|
|
|
- name: Build and push
|
|
uses: useblacksmith/build-push-action@v2
|
|
with:
|
|
context: .
|
|
push: true
|
|
provenance: false
|
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
|
|
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
|
|
tags: |
|
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.branch.outputs.name }}
|
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
|
|
|
|
# Generate version info for release builds
|
|
version:
|
|
name: Version
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
tag: ${{ steps.version.outputs.tag }}
|
|
short_sha: ${{ steps.version.outputs.short_sha }}
|
|
is_tag: ${{ steps.version.outputs.is_tag }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Generate version info
|
|
id: version
|
|
run: |
|
|
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
|
TAG=${GITHUB_REF#refs/tags/}
|
|
VERSION=${TAG#v}
|
|
echo "is_tag=true" >> $GITHUB_OUTPUT
|
|
else
|
|
VERSION=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
|
echo "is_tag=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
# Docker tags can't contain colons, so create a tag-safe version
|
|
TAG=$(echo "$VERSION" | tr ':' '-')
|
|
SHORT_SHA=${GITHUB_SHA::7}
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
|
|
|
# Build architecture-specific images in parallel (native runners)
|
|
build-release:
|
|
name: Build (${{ matrix.arch }})
|
|
needs: [test, version]
|
|
runs-on: ${{ matrix.runner }}
|
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: linux/amd64
|
|
arch: amd64
|
|
runner: blacksmith-4vcpu-ubuntu-2404
|
|
- platform: linux/arm64
|
|
arch: arm64
|
|
runner: blacksmith-4vcpu-ubuntu-2404-arm
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Blacksmith Builder
|
|
uses: useblacksmith/setup-docker-builder@v1
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: gmcintire
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Build and push
|
|
uses: useblacksmith/build-push-action@v2
|
|
with:
|
|
context: .
|
|
platforms: ${{ matrix.platform }}
|
|
push: true
|
|
provenance: false
|
|
build-args: |
|
|
VERSION=${{ needs.version.outputs.version }}
|
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ matrix.arch }}
|
|
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ matrix.arch }},mode=max
|
|
tags: |
|
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}-${{ matrix.arch }}
|
|
docker.io/${{ env.DOCKERHUB_IMAGE }}:${{ needs.version.outputs.tag }}-${{ matrix.arch }}
|
|
|
|
- name: Extract standalone binary
|
|
run: |
|
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}-${{ matrix.arch }}"
|
|
CONTAINER=$(docker create $IMAGE)
|
|
docker cp $CONTAINER:/usr/local/bin/towerops-agent ./towerops-agent-linux-${{ matrix.arch }}
|
|
docker rm $CONTAINER
|
|
sha256sum towerops-agent-linux-${{ matrix.arch }} > towerops-agent-linux-${{ matrix.arch }}.sha256
|
|
|
|
- name: Upload binary artifact
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: binary-${{ matrix.arch }}
|
|
path: |
|
|
towerops-agent-linux-${{ matrix.arch }}
|
|
towerops-agent-linux-${{ matrix.arch }}.sha256
|
|
|
|
# Create multi-arch manifest after both builds complete
|
|
manifest:
|
|
name: Create Manifest
|
|
needs: [version, build-release]
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: gmcintire
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Create and push manifests
|
|
run: |
|
|
TAG="${{ needs.version.outputs.tag }}"
|
|
SHORT_SHA="${{ needs.version.outputs.short_sha }}"
|
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
|
|
DH_IMAGE="docker.io/${{ env.DOCKERHUB_IMAGE }}"
|
|
|
|
# Create GHCR manifests
|
|
docker buildx imagetools create -t ${IMAGE}:${TAG} \
|
|
${IMAGE}:${TAG}-amd64 \
|
|
${IMAGE}:${TAG}-arm64
|
|
|
|
docker buildx imagetools create -t ${IMAGE}:latest \
|
|
${IMAGE}:${TAG}-amd64 \
|
|
${IMAGE}:${TAG}-arm64
|
|
|
|
docker buildx imagetools create -t ${IMAGE}:sha-${SHORT_SHA} \
|
|
${IMAGE}:${TAG}-amd64 \
|
|
${IMAGE}:${TAG}-arm64
|
|
|
|
# Create Docker Hub manifests
|
|
docker buildx imagetools create -t ${DH_IMAGE}:${TAG} \
|
|
${DH_IMAGE}:${TAG}-amd64 \
|
|
${DH_IMAGE}:${TAG}-arm64
|
|
|
|
docker buildx imagetools create -t ${DH_IMAGE}:latest \
|
|
${DH_IMAGE}:${TAG}-amd64 \
|
|
${DH_IMAGE}:${TAG}-arm64
|
|
|
|
docker buildx imagetools create -t ${DH_IMAGE}:sha-${SHORT_SHA} \
|
|
${DH_IMAGE}:${TAG}-amd64 \
|
|
${DH_IMAGE}:${TAG}-arm64
|
|
|
|
- name: Summary
|
|
run: |
|
|
echo "### Released version: ${{ needs.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "Multi-architecture build (amd64, arm64) - built in parallel" >> $GITHUB_STEP_SUMMARY
|
|
echo "Tags pushed to GHCR and Docker Hub:" >> $GITHUB_STEP_SUMMARY
|
|
echo "- \`latest\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "- \`${{ needs.version.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "- \`sha-${{ needs.version.outputs.short_sha }}\`" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Create GitHub Release with standalone binaries (only on tags)
|
|
release:
|
|
name: GitHub Release
|
|
needs: [version, manifest]
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Download amd64 binary
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: binary-amd64
|
|
|
|
- name: Download arm64 binary
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: binary-arm64
|
|
|
|
- name: Create GitHub Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
name: v${{ needs.version.outputs.version }}
|
|
generate_release_notes: true
|
|
files: |
|
|
towerops-agent-linux-amd64
|
|
towerops-agent-linux-amd64.sha256
|
|
towerops-agent-linux-arm64
|
|
towerops-agent-linux-arm64.sha256
|
|
|
|
- name: Notify agents of new release
|
|
if: success()
|
|
run: |
|
|
curl -sf -X POST \
|
|
-H "Authorization: Bearer ${{ secrets.AGENT_WEBHOOK_SECRET }}" \
|
|
https://towerops.net/api/v1/webhooks/agent-release
|
|
continue-on-error: true
|