Bump version to 0.2.0 #55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |