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
This commit is contained in:
2025-10-16 16:13:38 +02:00
parent 1601d979df
commit 5f19f10ad0
3 changed files with 127 additions and 41 deletions

View File

@@ -5,15 +5,21 @@ on:
branches: branches:
- '**' - '**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: permissions:
contents: write contents: read
packages: write
id-token: write
jobs: jobs:
release: release:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
timeout-minutes: 20
permissions:
contents: write
outputs: outputs:
new_release_published: ${{ steps.semantic.outputs.new_release_published }} new_release_published: ${{ steps.semantic.outputs.new_release_published }}
@@ -24,27 +30,20 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Import GPG key and configure signing - name: Import GPG key and configure signing
env: uses: crazy-max/ghaction-import-gpg@v6
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} with:
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 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: | run: |
echo "$GPG_PRIVATE_KEY" | gpg --batch --import git config --global user.name "Arlind-dev"
KEY_ID=$(gpg --list-secret-keys --keyid-format LONG | grep sec | tail -n1 | awk '{print $2}' | cut -d'/' -f2) git config --global user.email "arlind@sulej.ch"
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"
- name: Run semantic-release - name: Run semantic-release
id: semantic id: semantic
@@ -59,14 +58,20 @@ jobs:
conventional-changelog-conventionalcommits conventional-changelog-conventionalcommits
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_AUTHOR_NAME: semantic-release-bot GIT_AUTHOR_NAME: Arlind-dev
GIT_COMMITTER_NAME: semantic-release-bot GIT_COMMITTER_NAME: Arlind-dev
GIT_AUTHOR_EMAIL: arlind@sulej.ch GIT_AUTHOR_EMAIL: arlind@sulej.ch
GIT_COMMITTER_EMAIL: arlind@sulej.ch GIT_COMMITTER_EMAIL: arlind@sulej.ch
docker:
docker-prod:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: release needs: [release]
if: github.ref == 'refs/heads/main' && needs.release.outputs.new_release_published == 'true' if: github.ref == 'refs/heads/main' && needs.release.outputs.new_release_published == 'true'
timeout-minutes: 60
permissions:
contents: read
packages: write
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -82,8 +87,13 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Normalize repository owner to lowercase - name: Prepare build metadata
run: echo "OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV 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 - name: Build and push build-stage image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@@ -92,32 +102,108 @@ jobs:
file: Dockerfile.build file: Dockerfile.build
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
provenance: false provenance: mode=max
sbom: true
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
outputs: type=registry,oci-mediatypes=true
tags: | tags: |
ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:build-latest ghcr.io/${{ steps.meta.outputs.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-${{ needs.release.outputs.new_release_version }}
- name: Build and push main image - name: Build and push main image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386,linux/ppc64le,linux/riscv64,linux/s390x platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386,linux/ppc64le,linux/riscv64,linux/s390x
push: true push: true
provenance: false provenance: mode=max
sbom: true
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
outputs: type=registry,oci-mediatypes=true
build-args: | 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: | tags: |
ghcr.io/${{ env.OWNER_LC }}/${{ vars.IMAGE_NAME }}:latest ghcr.io/${{ steps.meta.outputs.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 }}:${{ 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: deploy:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: release needs: release
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
timeout-minutes: 10
permissions:
contents: read
steps: steps:
- name: Trigger Cloudflare Pages deploy - name: Trigger Cloudflare Pages deploy
run: | run: |

View File

@@ -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 FROM nginx:alpine
WORKDIR /usr/share/nginx/html
ARG BUILD_IMAGE
WORKDIR /usr/share/nginx/html
COPY --from=build /output ./ COPY --from=build /output ./
EXPOSE 80 EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

View File

@@ -2,14 +2,12 @@ FROM node:alpine AS build
WORKDIR /app 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 ./ COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile RUN pnpm install --frozen-lockfile
COPY . . COPY . .
RUN pnpm run build RUN pnpm run build
RUN mkdir -p /output && cp -r /app/build/. /output/ RUN mkdir -p /output && cp -r /app/build/. /output/