Skip to content

Bump version to 0.2.0 #55

Bump version to 0.2.0

Bump version to 0.2.0 #55

# This workflow builds and pushes a Docker image to GHCR and Docker Hub
# under multiple repository aliases (auto_docker_proxy and traefik_network_connector).
#
# Triggers:
# 1. Push to 'main' branch (tags as 'latest')
# 2. Push of tags 'v*.*.*' (tags as SemVer)
# 3. Pull Requests (builds 'pr-XXX' tag, only if author is trusted or PR is approved)
#
# Features:
# - Multi-platform build
# - Multi-registry push (GHCR & Docker Hub)
# - GitHub Actions cache
# - Cosign OIDC signing for main/tag pushes
name: Build and Push Docker (Multi-Repo Alias)
on:
push:
branches:
- main
tags:
- 'v*.*.*' # Trigger on version tags like v1.0.0
pull_request:
types: [opened, synchronize, reopened]
jobs:
build-push-sign:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Required for OIDC signing
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# --- PR Security Checks (Start) ---
- name: Determine if PR author is trusted
if: ${{ github.event_name == 'pull_request' }}
id: check-author
shell: bash
run: |
# Define a list of trusted authors. Consider using a GitHub Team for larger projects.
trusted_authors=("obeone")
trusted="false"
for author in "${trusted_authors[@]}"; do
if [[ "$author" == "$GITHUB_ACTOR" ]]; then
trusted="true"
break
fi
done
echo "Author ($GITHUB_ACTOR) trusted: $trusted"
echo "trusted=$trusted" >> "$GITHUB_OUTPUT"
- name: Check for admin approval
if: ${{ github.event_name == 'pull_request' }}
id: check-approval
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
# Check for approval by an organization OWNER or MEMBER.
APPROVALS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/reviews \
--jq '.[] | select(.state=="APPROVED" and (.author_association=="OWNER" or .author_association=="MEMBER")) | .user.login')
if [[ -n "$APPROVALS" ]]; then
echo "PR is approved by an organization member: $APPROVALS"
echo "approved=true" >> "$GITHUB_OUTPUT"
else
echo "PR is not approved by an organization member."
echo "approved=false" >> "$GITHUB_OUTPUT"
fi
# --- PR Security Checks (End) ---
- name: Security Gatekeeper
id: gatekeeper
shell: bash
run: |
# This step consolidates the logic to determine if the build should proceed.
# The build will run if any of the following conditions are met:
# 1. The event is a 'push' (to 'main' branch or a tag).
# 2. The event is a 'pull_request' AND the author is explicitly trusted.
# 3. The event is a 'pull_request' AND the PR has been approved by an administrator.
if [[ "${{ github.event_name }}" == "push" ]]; then
echo "Event is 'push', proceeding with build."
echo "run_build=true" >> "$GITHUB_OUTPUT"
elif [[ "${{ steps.check-author.outputs.trusted }}" == "true" ]]; then
echo "PR author is trusted, proceeding with build."
echo "run_build=true" >> "$GITHUB_OUTPUT"
elif [[ "${{ steps.check-approval.outputs.approved }}" == "true" ]]; then
echo "PR is approved by an admin, proceeding with build."
echo "run_build=true" >> "$GITHUB_OUTPUT"
else
echo "PR event does not meet security criteria. Build will be skipped."
echo "run_build=false" >> "$GITHUB_OUTPUT"
fi
- name: Docker metadata (multi-repo)
id: meta
uses: docker/metadata-action@v5
with:
# Define all four image names. The generated tags will be applied to each of them.
images: |
ghcr.io/obeone/auto_docker_proxy
docker.io/obeoneorg/auto_docker_proxy
ghcr.io/obeone/traefik_network_connector
docker.io/obeoneorg/traefik_network_connector
tags: |
# For pushes to the 'main' branch, tag the image as 'latest'.
type=ref,event=branch,enable=${{ github.ref_name == 'main' }},prefix=,suffix=latest
# For 'v*.*.*' tags, generate SemVer tags (e.g., v1.2.3, v1.2, v1).
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# For 'pull_request' events, tag the image as 'pr-XXX' (where XXX is the PR number).
type=ref,event=pr
- name: Build and push (multi-repo)
if: steps.gatekeeper.outputs.run_build == 'true'
id: build-and-push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
platforms: |
linux/amd64
linux/arm64
linux/i386
linux/armhf
linux/armel
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
# Pass the clean version (e.g., 1.0.0) extracted from metadata to the Dockerfile.
VERSION=${{ steps.meta.outputs.version }}
- name: Set up cosign
if: steps.gatekeeper.outputs.run_build == 'true'
uses: sigstore/cosign-installer@v3
- name: Sign the container image with cosign
# This step only signs official images, which are those built from 'main' branch pushes or tag pushes.
if: >-
${{
steps.gatekeeper.outputs.run_build == 'true' &&
github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
}}
env:
COSIGN_EXPERIMENTAL: true
# Retrieve the image digest from the previous build step.
DIGEST: ${{ steps.build-and-push.outputs.digest }}
shell: bash
run: |
if [ -z "${DIGEST}" ]; then
echo "Digest is empty, aborting image signing."
exit 1
fi
echo "Signing digest: ${DIGEST}"
# List all four base image names that need to be signed.
IMAGES=(
"ghcr.io/obeone/auto_docker_proxy"
"docker.io/obeoneorg/auto_docker_proxy"
"ghcr.io/obeone/traefik_network_connector"
"docker.io/obeoneorg/traefik_network_connector"
)
for image in "${IMAGES[@]}"; do
echo "Signing ${image}@${DIGEST}"
cosign sign --yes "${image}@${DIGEST}"
done