diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 969fb7f..6a7760b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,6 +3,10 @@ # ============================================================================= # Monitors Docker base images for updates # Combined with docker-maintenance.yml workflow for auto-merge and release +# +# The commit prefix decides whether a merged update is released: fix(...) +# cuts a PATCH release and rebuilds the image, chore(...) releases nothing. +# Everything that ends up in the shipped image uses fix(...). # ============================================================================= version: 2 @@ -19,11 +23,33 @@ updates: - "dependencies" - "docker" - "dependabot" + # No include: "scope" - the prefix already has one, and Dependabot would + # append "(deps)" to it (fix(docker)(deps): ...). commit-message: prefix: "fix(docker)" - include: "scope" open-pull-requests-limit: 5 + # Python packages installed into the image (src/requirements.txt). + # Version updates are off (limit 0) as before; the entry sets the commit + # prefix of Dependabot's security updates, which are not subject to the + # limit - without it they arrive as chore(deps) and a merged security fix + # is never released. + - package-ecosystem: "pip" + directory: "/src" + schedule: + interval: "weekly" + day: "sunday" + time: "06:30" + timezone: "Etc/UTC" + labels: + - "dependencies" + - "python" + - "dependabot" + commit-message: + prefix: "fix(deps)" + prefix-development: "chore(deps-dev)" + open-pull-requests-limit: 0 + # GitHub Actions updates - package-ecosystem: "github-actions" directory: "/" diff --git a/.github/workflows/docker-maintenance.yml b/.github/workflows/docker-maintenance.yml index 1f8272e..3a174d9 100644 --- a/.github/workflows/docker-maintenance.yml +++ b/.github/workflows/docker-maintenance.yml @@ -5,9 +5,13 @@ # merges only after docker-release.yml built the image on the PR (base # image and pip updates in src/); everything else stays open with an # annotation. -# After merge, the docker-release.yml workflow is triggered which: +# A merge made with the GITHUB_TOKEN starts no push workflow, so after the +# merge this workflow dispatches docker-release.yml on main and waits for +# it (release-workflow). That run builds the merged commit again and only +# then: # 1. Creates a semantic release # 2. Builds and pushes the new Docker image +# A release run that does not succeed turns this job red. # # Flow: # ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ @@ -33,8 +37,11 @@ on: # changes under .github/ always stay open for review. permissions: - contents: write - pull-requests: write + contents: write # merge + pull-requests: write # approve, read the PR + checks: read # check runs and suites of the PR head commit + statuses: read # commit statuses of the PR head commit + actions: write # dispatch the release; read workflow runs jobs: maintenance: @@ -48,4 +55,8 @@ jobs: # image in src/Dockerfile and the pip packages in src/requirements.txt # that the build installs; a PR outside them stays open (not tested). required-workflows: '.github/workflows/docker-release.yml' + # After a merge - and only then - dispatch the release on main and + # wait for it: it rebuilds the merged commit before it releases it, + # and a failed release run fails this job. + release-workflow: '.github/workflows/docker-release.yml' secrets: inherit diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 7bdd61f..915e28d 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -62,30 +62,68 @@ jobs: severity: 'warning' # ============================================ - # Release Job (only on main branch push) + # Image Build Gate (every event, never pushed) + # ============================================ + + # Builds the image from this commit on every event - the PR CI that + # docker-maintenance.yml waits for, and on a push or dispatch the gate + # of the release: a failed build skips the release and the image push. + docker-build-validate: + name: 🔨 Validate Docker Build + needs: [validate-compose, validate-scripts] + if: | + needs.validate-compose.result == 'success' && + needs.validate-scripts.result == 'success' + uses: bauer-group/automation-templates/.github/workflows/docker-build.yml@main + with: + publish-to: 'ghcr' + ghcr-image-name: 'bauer-group/CS-ClientPublicIP/clientpublicip' + + auto-tags: true + dockerfile-path: './src/Dockerfile' + docker-context: './src' + platforms: 'linux/amd64' + + push: false + + security-scan: false + security-fail-on: 'CRITICAL' + secrets: inherit + + # ============================================ + # Release Job (push to main or dispatch) # ============================================ release: name: 📦 Create Semantic Release - needs: [validate-compose, validate-scripts] + needs: [validate-compose, validate-scripts, docker-build-validate] if: | (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && needs.validate-compose.result == 'success' && - needs.validate-scripts.result == 'success' + needs.validate-scripts.result == 'success' && + needs.docker-build-validate.result == 'success' uses: bauer-group/automation-templates/.github/workflows/modules-semantic-release.yml@main with: target-branch: 'main' + # Release the commit this run validated and built - the image job + # below builds it too. Unpinned, the release would take the tip of + # main: a push that landed meanwhile would be tagged untested and be + # missing from the image. Pinned, nothing is released once main has + # moved on, and the run for the newer main releases both. + ref: ${{ github.sha }} dry-run: false force-release: ${{ inputs.force-release || false }} secrets: inherit # ============================================ - # Docker Build Jobs + # Docker Publish Job (only after a release) # ============================================ docker-build-release: name: 🐳 Build & Push Docker Image needs: release + # Only a run that created a release publishes - never a PR, a branch + # dispatch or a run that released nothing. if: needs.release.outputs.release-created == 'true' uses: bauer-group/automation-templates/.github/workflows/docker-build.yml@main with: @@ -111,26 +149,3 @@ jobs: generate-sbom: true sync-dockerhub-readme: true secrets: inherit - - docker-build-pr: - name: 🔨 Validate Docker Build (PR) - needs: [validate-compose, validate-scripts] - if: | - github.event_name == 'pull_request' && - needs.validate-compose.result == 'success' && - needs.validate-scripts.result == 'success' - uses: bauer-group/automation-templates/.github/workflows/docker-build.yml@main - with: - publish-to: 'ghcr' - ghcr-image-name: 'bauer-group/CS-ClientPublicIP/clientpublicip' - - auto-tags: true - dockerfile-path: './src/Dockerfile' - docker-context: './src' - platforms: 'linux/amd64' - - push: false - - security-scan: false - security-fail-on: 'CRITICAL' - secrets: inherit