From 5f19f10ad0683e590e14f2d5e25bf4ef89572223 Mon Sep 17 00:00:00 2001 From: Arlind Sulejmani Date: Thu, 16 Oct 2025 16:13:38 +0200 Subject: [PATCH] chore(ci): rework image creation add a dev build chore(ci): enable provenance and enrich OCI metadata for docker images chore(ci): align dev and prod docker builds with multi-arch release chore(ci): enable provenance mode=max for image build chore(ci): fix multi-arch build to support provenance mode=max chore(docker): install corepack in build image with npm chore(ci): use action for gpg import --- .github/workflows/main.yml | 156 ++++++++++++++++++++++++++++--------- Dockerfile | 8 +- Dockerfile.build | 4 +- 3 files changed, 127 insertions(+), 41 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index da250ee..c3de71a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,15 +5,21 @@ on: branches: - '**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: - contents: write - packages: write - id-token: write + contents: read jobs: release: runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/main' + timeout-minutes: 20 + + permissions: + contents: write outputs: new_release_published: ${{ steps.semantic.outputs.new_release_published }} @@ -24,27 +30,20 @@ jobs: uses: actions/checkout@v5 with: fetch-depth: 0 + - name: Import GPG key and configure signing - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + git_config_global: true + + - name: Configure git user run: | - echo "$GPG_PRIVATE_KEY" | gpg --batch --import - KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | tail -n1 | awk '{print $2}' | cut -d'/' -f2) - - echo "use-agent" >> ~/.gnupg/gpg.conf - echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf - echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf - - git config --global user.signingkey "$KEY_ID" - git config --global commit.gpgsign true - git config --global gpg.program gpg - git config --global gpg.format openpgp - - export GPG_TTY=$(tty) - echo "test" | gpg --batch --yes --passphrase "$GPG_PASSPHRASE" --pinentry-mode loopback -u "$KEY_ID" -s >/dev/null - - echo "Using GPG key: $KEY_ID" + git config --global user.name "Arlind-dev" + git config --global user.email "arlind@sulej.ch" - name: Run semantic-release id: semantic @@ -59,14 +58,20 @@ jobs: conventional-changelog-conventionalcommits env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GIT_AUTHOR_NAME: semantic-release-bot - GIT_COMMITTER_NAME: semantic-release-bot + GIT_AUTHOR_NAME: Arlind-dev + GIT_COMMITTER_NAME: Arlind-dev GIT_AUTHOR_EMAIL: arlind@sulej.ch GIT_COMMITTER_EMAIL: arlind@sulej.ch - docker: + + docker-prod: runs-on: ubuntu-24.04 - needs: release + needs: [release] if: github.ref == 'refs/heads/main' && needs.release.outputs.new_release_published == 'true' + timeout-minutes: 60 + + permissions: + contents: read + packages: write steps: - name: Checkout repository @@ -82,8 +87,13 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Normalize repository owner to lowercase - run: echo "OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV + - name: Prepare build metadata + id: meta + run: | + echo "owner_lc=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT" + echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + + - name: Build and push build-stage image uses: docker/build-push-action@v6 @@ -92,32 +102,108 @@ jobs: file: Dockerfile.build platforms: linux/amd64 push: true - provenance: false + provenance: mode=max + sbom: true cache-from: type=gha cache-to: type=gha,mode=max + outputs: type=registry,oci-mediatypes=true tags: | - ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:build-latest - ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:build-${{ needs.release.outputs.new_release_version }} + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:build-latest + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:build-${{ needs.release.outputs.new_release_version }} - name: Build and push main image uses: docker/build-push-action@v6 with: context: . + file: Dockerfile platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386,linux/ppc64le,linux/riscv64,linux/s390x push: true - provenance: false + provenance: mode=max + sbom: true cache-from: type=gha cache-to: type=gha,mode=max + outputs: type=registry,oci-mediatypes=true build-args: | - BUILD_IMAGE=ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:build-latest + BUILD_IMAGE=ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:build-${{ needs.release.outputs.new_release_version }} tags: | - ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:latest - ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:${{ needs.release.outputs.new_release_version }} + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:latest + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:${{ needs.release.outputs.new_release_version }} + + docker-dev: + runs-on: ubuntu-24.04 + if: github.ref != 'refs/heads/main' + timeout-minutes: 60 + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - 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: Prepare metadata and branch vars + id: meta + run: | + echo "owner_lc=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT" + BRANCH_NAME=${GITHUB_REF#refs/heads/} + SANITIZED_BRANCH=$(echo "$BRANCH_NAME" | tr '/' '-' | tr '_' '-' | tr -cd '[:alnum:]-') + echo "sanitized_branch=$SANITIZED_BRANCH" >> "$GITHUB_OUTPUT" + echo "sha_short=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + + + + - name: Build and push build-stage image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.build + platforms: linux/amd64 + push: true + provenance: mode=max + sbom: true + cache-from: type=gha + cache-to: type=gha,mode=max + outputs: type=registry,oci-mediatypes=true + tags: | + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:build-${{ steps.meta.outputs.sanitized_branch }}-${{ steps.meta.outputs.sha_short }} + + - name: Build and push main image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386,linux/ppc64le,linux/riscv64,linux/s390x + push: true + provenance: mode=max + sbom: true + cache-from: type=gha + cache-to: type=gha,mode=max + outputs: type=registry,oci-mediatypes=true + build-args: | + BUILD_IMAGE=ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:build-${{ steps.meta.outputs.sanitized_branch }}-${{ steps.meta.outputs.sha_short }} + tags: | + ghcr.io/${{ steps.meta.outputs.owner_lc }}/${{ vars.IMAGE_NAME }}:${{ steps.meta.outputs.sanitized_branch }}-${{ steps.meta.outputs.sha_short }} deploy: runs-on: ubuntu-24.04 needs: release if: github.ref == 'refs/heads/main' + timeout-minutes: 10 + + permissions: + contents: read steps: - name: Trigger Cloudflare Pages deploy run: | diff --git a/Dockerfile b/Dockerfile index 9c65397..3598370 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,12 @@ -FROM ghcr.io/arlind-dev/sulej.ch:build-latest AS build +ARG BUILD_IMAGE=ghcr.io/arlind-dev/sulej.ch:build-latest +FROM --platform=$BUILDPLATFORM ${BUILD_IMAGE} AS build FROM nginx:alpine -WORKDIR /usr/share/nginx/html +ARG BUILD_IMAGE + +WORKDIR /usr/share/nginx/html COPY --from=build /output ./ EXPOSE 80 - CMD ["nginx", "-g", "daemon off;"] diff --git a/Dockerfile.build b/Dockerfile.build index be667ec..b21fc15 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -2,14 +2,12 @@ FROM node:alpine AS build WORKDIR /app -RUN corepack enable && corepack prepare pnpm@latest --activate +RUN npm install -g --force corepack && corepack enable && corepack prepare pnpm@latest --activate COPY package.json pnpm-lock.yaml ./ - RUN pnpm install --frozen-lockfile COPY . . - RUN pnpm run build RUN mkdir -p /output && cp -r /app/build/. /output/