diff --git a/.github/workflows/container-validation.yml b/.github/workflows/container-validation.yml index 82763ca..6ad3cb4 100644 --- a/.github/workflows/container-validation.yml +++ b/.github/workflows/container-validation.yml @@ -5,6 +5,9 @@ on: pull_request: branches: [ master, testing ] +permissions: + contents: read + jobs: simplerisk-jammy: name: 'Verify simplerisk/simplerisk image based on Ubuntu 22.04 (Jammy)' @@ -13,7 +16,7 @@ jobs: context_path: "simplerisk/" dockerfile_path: "simplerisk/Dockerfile" image_tag: "simplerisk/simplerisk:testing" - build_args: "ubuntu_version_code=jammy" + build_args: "ubuntu_version_code=jammy\nPREGA_BUNDLE_FALLBACK=true" simplerisk-noble: name: 'Verify simplerisk/simplerisk image based on Ubuntu 24.04 (Noble)' @@ -22,7 +25,7 @@ jobs: context_path: "simplerisk/" dockerfile_path: "simplerisk/Dockerfile" image_tag: "simplerisk/simplerisk:testing" - build_args: "ubuntu_version_code=noble" + build_args: "ubuntu_version_code=noble\nPREGA_BUNDLE_FALLBACK=true" simplerisk-minimal-php83: name: 'Verify simplerisk/simplerisk-minimal image based on PHP 8.3 with Apache' diff --git a/.github/workflows/create_new_tag.yml b/.github/workflows/create_new_tag.yml index 00a15b4..bbdf2b0 100644 --- a/.github/workflows/create_new_tag.yml +++ b/.github/workflows/create_new_tag.yml @@ -4,6 +4,9 @@ on: push: branches: [ "master" ] +permissions: + contents: write # git tag + git push --tags + jobs: create-release: runs-on: ubuntu-latest diff --git a/.github/workflows/promote-latest.yml b/.github/workflows/promote-latest.yml index 1270782..d8af9f9 100644 --- a/.github/workflows/promote-latest.yml +++ b/.github/workflows/promote-latest.yml @@ -1,22 +1,36 @@ name: Promote latest image tag (release) -# GA promotion for simplerisk-minimal, run MANUALLY (workflow_dispatch) once the -# release has merged to master. Build-once model: the RC image was already built -# by publish-testing.yml at the testing cut (tags -php83/84/85 + bare -# ). GA does NOT rebuild — it (1) retags DockerHub :latest to the -# existing -php85 digest via `buildx imagetools create` (multi-arch -# preserved), and (2) writes SSM /simplerisk/customers/image-tag/latest = -# -php85 in the customers account (OIDC), so the image-updater Lambda -# rolls tier=latest (production) services onto the promoted digest. +# GA promotion, run MANUALLY (workflow_dispatch) once the release has merged to +# master. Build-once model: both release images were already built by +# publish-testing.yml at the testing cut. GA does NOT rebuild anything -- it +# repoints tags and parameters at those existing digests, so the bytes validated +# in testing are byte-identical to the bytes that reach production. +# +# 1. Docker Hub :latest -> the existing RC digest, for both +# simplerisk/simplerisk-minimal (-php85) and simplerisk/simplerisk +# (-noble), via `buildx imagetools create` (multi-arch preserved). +# 2. GHCR mirror -- copies the same digests to ghcr.io, cosign-signed, so the +# GHCR and Docker Hub images for a version are the same bytes. GHCR used to +# get its own rebuild from the prod bundle, which meant ghcr and +# dockerhub were different images sharing a name. +# 3. SSM /simplerisk/customers/image-tag/latest = -php85 in the +# customers account (OIDC), so the image-updater Lambda rolls tier=latest +# (production) services onto the promoted digest. # # See design code-development docs/superpowers/specs/2026-07-10-release-image-promotion-design. on: workflow_dispatch: + inputs: + skip_full_image: + description: 'Transitional: skip simplerisk/simplerisk (no RC digest for releases cut before the full-stack RC build landed)' + type: boolean + default: false permissions: contents: read - id-token: write + id-token: write # OIDC: AWS role + cosign/fulcio identity challenge + packages: write # GHCR mirror concurrency: group: promote-latest @@ -25,14 +39,24 @@ concurrency: env: AWS_REGION: us-east-1 SSM_PARAM: /simplerisk/customers/image-tag/latest + MINIMAL_IMAGE: simplerisk/simplerisk-minimal + FULL_IMAGE: simplerisk/simplerisk + GHCR_MINIMAL: ghcr.io/simplerisk/simplerisk-minimal + GHCR_FULL: ghcr.io/simplerisk/simplerisk jobs: promote: + environment: release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 + - name: Install cosign + uses: sigstore/cosign-installer@v3.5.0 + with: + cosign-release: 'v2.4.0' + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -42,6 +66,13 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Read release version from the minimal Dockerfile id: ver run: | @@ -55,32 +86,81 @@ jobs: fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - name: Verify the RC image exists, then retag :latest → -php85 + - name: Promote simplerisk-minimal — :latest → -php85 env: VERSION: ${{ steps.ver.outputs.version }} - IMAGE_NAME: simplerisk/simplerisk-minimal run: | set -euo pipefail # The php85 immutable tag MUST already exist (built by publish-testing # at the RC cut). Fail loudly rather than silently promoting nothing. - if ! docker buildx imagetools inspect "${IMAGE_NAME}:${VERSION}-php85" >/dev/null 2>&1; then - echo "::error::${IMAGE_NAME}:${VERSION}-php85 not found on Docker Hub — was the RC published (Plan 1)?"; exit 1 + if ! docker buildx imagetools inspect "${MINIMAL_IMAGE}:${VERSION}-php85" >/dev/null 2>&1; then + echo "::error::${MINIMAL_IMAGE}:${VERSION}-php85 not found on Docker Hub — was the RC published?"; exit 1 fi # Currency guard: only promote the version that is CURRENTLY in testing. # :testing floats to the current RC (publish-testing tags -php85 and # :testing on the same build), so the digests match iff VERSION is the # current RC. Prevents a stale committed Dockerfile version (or a stale # dispatch ref) from promoting an old-but-existing release to prod :latest. - SRC_DIGEST=$(docker buildx imagetools inspect "${IMAGE_NAME}:${VERSION}-php85" --format '{{.Manifest.Digest}}') - TESTING_DIGEST=$(docker buildx imagetools inspect "${IMAGE_NAME}:testing" --format '{{.Manifest.Digest}}') + SRC_DIGEST=$(docker buildx imagetools inspect "${MINIMAL_IMAGE}:${VERSION}-php85" --format '{{.Manifest.Digest}}') + TESTING_DIGEST=$(docker buildx imagetools inspect "${MINIMAL_IMAGE}:testing" --format '{{.Manifest.Digest}}') if [ "$SRC_DIGEST" != "$TESTING_DIGEST" ]; then - echo "::error::${IMAGE_NAME}:${VERSION}-php85 ($SRC_DIGEST) is not the current testing RC ($TESTING_DIGEST) — refusing to promote a stale version to :latest"; exit 1 + echo "::error::${MINIMAL_IMAGE}:${VERSION}-php85 ($SRC_DIGEST) is not the current testing RC ($TESTING_DIGEST) — refusing to promote a stale version to :latest"; exit 1 fi # Retag (no rebuild): create :latest from the existing multi-arch digest. docker buildx imagetools create \ - --tag "${IMAGE_NAME}:latest" \ - "${IMAGE_NAME}:${VERSION}-php85" - echo "retagged ${IMAGE_NAME}:latest -> ${VERSION}-php85 digest" >> "$GITHUB_STEP_SUMMARY" + --tag "${MINIMAL_IMAGE}:latest" \ + "${MINIMAL_IMAGE}:${VERSION}-php85" + echo "retagged ${MINIMAL_IMAGE}:latest -> ${VERSION}-php85 ($SRC_DIGEST)" >> "$GITHUB_STEP_SUMMARY" + + - name: Promote simplerisk (full-stack) — :latest → -noble + if: ${{ !inputs.skip_full_image }} + env: + VERSION: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + if ! docker buildx imagetools inspect "${FULL_IMAGE}:${VERSION}-noble" >/dev/null 2>&1; then + echo "::error::${FULL_IMAGE}:${VERSION}-noble not found on Docker Hub — was the RC published? (re-run with skip_full_image for a release cut before the full-stack RC build landed)"; exit 1 + fi + SRC_DIGEST=$(docker buildx imagetools inspect "${FULL_IMAGE}:${VERSION}-noble" --format '{{.Manifest.Digest}}') + TESTING_DIGEST=$(docker buildx imagetools inspect "${FULL_IMAGE}:testing" --format '{{.Manifest.Digest}}') + if [ "$SRC_DIGEST" != "$TESTING_DIGEST" ]; then + echo "::error::${FULL_IMAGE}:${VERSION}-noble ($SRC_DIGEST) is not the current testing RC ($TESTING_DIGEST) — refusing to promote a stale version to :latest"; exit 1 + fi + docker buildx imagetools create \ + --tag "${FULL_IMAGE}:latest" \ + "${FULL_IMAGE}:${VERSION}-noble" + echo "retagged ${FULL_IMAGE}:latest -> ${VERSION}-noble ($SRC_DIGEST)" >> "$GITHUB_STEP_SUMMARY" + + - name: Mirror the promoted digests to GHCR (cosign-signed) + env: + VERSION: ${{ steps.ver.outputs.version }} + SKIP_FULL: ${{ inputs.skip_full_image }} + run: | + set -euo pipefail + # imagetools create copies the manifest (and blobs) across registries, + # so GHCR receives the identical digest rather than a rebuild. Each + # source digest is mirrored once, carrying every tag that points at it. + # cosign signs the digest (not the tag), so one signature per call. + mirror() { + local src="$1" dst="$2"; shift 2 + local args=() t digest + for t in "$@"; do args+=(--tag "${dst}:${t}"); done + docker buildx imagetools create "${args[@]}" "$src" + digest=$(docker buildx imagetools inspect "${dst}:${1}" --format '{{.Manifest.Digest}}') + cosign sign --yes "${dst}@${digest}" + echo "mirrored $src -> ${dst} [$*] ($digest)" >> "$GITHUB_STEP_SUMMARY" + } + + mirror "${MINIMAL_IMAGE}:${VERSION}-php83" "${GHCR_MINIMAL}" "${VERSION}-php83" + mirror "${MINIMAL_IMAGE}:${VERSION}-php84" "${GHCR_MINIMAL}" "${VERSION}-php84" + mirror "${MINIMAL_IMAGE}:${VERSION}-php85" "${GHCR_MINIMAL}" "${VERSION}-php85" "${VERSION}" "latest" + + if [ "$SKIP_FULL" != "true" ]; then + mirror "${FULL_IMAGE}:${VERSION}-jammy" "${GHCR_FULL}" "${VERSION}-jammy" + mirror "${FULL_IMAGE}:${VERSION}-noble" "${GHCR_FULL}" "${VERSION}-noble" "${VERSION}" "latest" + else + echo "skip_full_image set — ${GHCR_FULL} not mirrored for ${VERSION}" >> "$GITHUB_STEP_SUMMARY" + fi - name: Configure AWS credentials (OIDC → customers account) uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/publish-testing.yml b/.github/workflows/publish-testing.yml index e9da8c3..4d63c49 100644 --- a/.github/workflows/publish-testing.yml +++ b/.github/workflows/publish-testing.yml @@ -84,6 +84,7 @@ jobs: publish: name: Publish simplerisk-minimal RC images needs: resolve + environment: testing runs-on: ubuntu-latest # 3 sequential multi-arch (arm64 = QEMU-emulated, slow) variant builds run here. timeout-minutes: 120 @@ -203,6 +204,7 @@ jobs: publish-full: name: Publish simplerisk (full-stack) RC images needs: resolve + environment: testing runs-on: ubuntu-latest timeout-minutes: 90 permissions: diff --git a/.github/workflows/push-to-dockerhub.yml b/.github/workflows/push-to-dockerhub.yml deleted file mode 100644 index 82474bc..0000000 --- a/.github/workflows/push-to-dockerhub.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Push images to DockerHub - -on: - workflow_dispatch: - push: - branches: [ "master" ] - # Publish semver tags as releases. - #tags: [ '[2022]0701-001' ] - -# On a job that uses a reusable workflow, it seems you cannot -# use env on a with block (https://github.com/actions/runner/issues/1189#issuecomment-1741672276) -env: - VERSION: "20240102-001" - -jobs: - simplerisk-jammy: - name: 'Push simplerisk/simplerisk image based on Ubuntu 22.04 (Jammy)' - uses: ./.github/workflows/push-to-dockerhub_rw.yml - with: - context_path: "simplerisk" - dockerfile_path: "simplerisk/Dockerfile" - image_name: "simplerisk/simplerisk" - version: "20260519-001" - os_version: "jammy" - build_args: "ubuntu_version_code=jammy" - secrets: inherit - simplerisk-noble: - name: 'Push simplerisk/simplerisk image based on Ubuntu 24.04 (Noble)' - uses: ./.github/workflows/push-to-dockerhub_rw.yml - with: - context_path: "simplerisk" - dockerfile_path: "simplerisk/Dockerfile" - image_name: "simplerisk/simplerisk" - version: "20260519-001" - os_version: "noble" - main_image: true - build_args: "ubuntu_version_code=noble" - secrets: inherit diff --git a/.github/workflows/push-to-dockerhub_rw.yml b/.github/workflows/push-to-dockerhub_rw.yml deleted file mode 100644 index 16e66b7..0000000 --- a/.github/workflows/push-to-dockerhub_rw.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Push resulting image to DockerHub - -on: - workflow_call: - inputs: - context_path: - description: Base path to perform the build - required: true - type: string - dockerfile_path: - description: Path where Dockerfile is located - required: true - type: string - image_name: - description: What would be the name of the image - required: true - type: string - version: - description: SimpleRisk version to upload - required: true - type: string - os_version: - description: Type of base image to put on the tag - required: true - type: string - main_image: - description: Is this the latest image? - default: false - type: boolean - build_args: - description: Arguments to use on image at runtime - type: string - platforms: - description: Target platforms for the build (e.g. linux/amd64,linux/arm64) - default: linux/amd64 - type: string - secrets: - DOCKER_USERNAME: - required: true - DOCKER_TOKEN: - required: true - -jobs: - dockerhub: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Log in to Docker Hub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_TOKEN }} - - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@v6 - with: - images: ${{ inputs.image_name }} - - - name: Build and push main Docker image - if: ${{ inputs.main_image }} - id: build-and-push-main - uses: docker/build-push-action@v7 - with: - context: ${{ inputs.context_path }} - file: ${{ inputs.dockerfile_path }} - push: ${{ github.event_name != 'pull_request' }} - build-args: ${{ inputs.build_args || '' }} - platforms: ${{ inputs.platforms }} - tags: | - ${{ inputs.image_name }} - ${{ inputs.image_name }}:${{ inputs.version }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ inputs.os_version }} - cache-to: type=gha,mode=max,scope=${{ inputs.os_version }} - - - name: Build and push specific Docker image - id: build-and-push-spec - uses: docker/build-push-action@v7 - with: - context: ${{ inputs.context_path }} - file: ${{ inputs.dockerfile_path }} - push: ${{ github.event_name != 'pull_request' }} - build-args: ${{ inputs.build_args || '' }} - platforms: ${{ inputs.platforms }} - tags: | - ${{ inputs.image_name }}:${{ inputs.version }}-${{ inputs.os_version }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha,scope=${{ inputs.os_version }} - cache-to: type=gha,mode=max,scope=${{ inputs.os_version }} - diff --git a/.github/workflows/push-to-gh-pkgs.yml b/.github/workflows/push-to-gh-pkgs.yml deleted file mode 100644 index 63e5a7f..0000000 --- a/.github/workflows/push-to-gh-pkgs.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Push images to GitHub Packages - -on: - workflow_dispatch: - push: - branches: [ "master" ] - # Publish semver tags as releases. - #tags: [ '[2022]0701-001' ] - -# On a job that uses a reusable workflow, it seems you cannot -# use env on a with block (https://github.com/actions/runner/issues/1189#issuecomment-1741672276) -env: - VERSION: "20240102-001" - -jobs: - simplerisk-jammy: - name: 'Push simplerisk/simplerisk image based on Ubuntu 22.04 (Jammy)' - uses: ./.github/workflows/push-to-gh-pkgs_rw.yml - with: - context_path: "simplerisk" - dockerfile_path: "simplerisk/Dockerfile" - image_name: "simplerisk" - version: "20260519-001" - os_version: "jammy" - build_args: "ubuntu_version_code=jammy" - secrets: inherit - simplerisk-noble: - name: 'Push simplerisk/simplerisk image based on Ubuntu 24.04 (Noble)' - uses: ./.github/workflows/push-to-gh-pkgs_rw.yml - with: - context_path: "simplerisk" - dockerfile_path: "simplerisk/Dockerfile" - image_name: "simplerisk" - version: "20260519-001" - os_version: "noble" - main_image: true - build_args: "ubuntu_version_code=noble" - secrets: inherit - simplerisk-minimal-php84: - name: 'Push simplerisk/simplerisk-minimal image based on PHP 8.3 with Apache' - uses: ./.github/workflows/push-to-gh-pkgs_rw.yml - with: - context_path: "simplerisk-minimal" - dockerfile_path: "simplerisk-minimal/Dockerfile" - image_name: "simplerisk-minimal" - version: "20260519-001" - os_version: "php83" - build_args: "php_version=8.3" - secrets: inherit - simplerisk-minimal-php85: - name: 'Push simplerisk/simplerisk-minimal image based on PHP 8.4 with Apache' - uses: ./.github/workflows/push-to-gh-pkgs_rw.yml - with: - context_path: "simplerisk-minimal" - dockerfile_path: "simplerisk-minimal/Dockerfile" - image_name: "simplerisk-minimal" - version: "20260519-001" - os_version: "php84" - main_image: true - build_args: "php_version=8.4" - secrets: inherit diff --git a/.github/workflows/push-to-gh-pkgs_rw.yml b/.github/workflows/push-to-gh-pkgs_rw.yml deleted file mode 100644 index 1e53003..0000000 --- a/.github/workflows/push-to-gh-pkgs_rw.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Push resulting image to Github Packages - -on: - workflow_call: - inputs: - context_path: - required: true - type: string - dockerfile_path: - required: true - type: string - image_name: - required: true - type: string - version: - required: true - type: string - os_version: - required: true - type: string - main_image: - default: false - type: boolean - build_args: - type: string - -env: - # Use docker.io for Docker Hub if empty - REGISTRY: ghcr.io - IMAGE_NAME: "${{ github.repository_owner }}/simplerisk" - -jobs: - github_packages: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install cosign - if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@v3.5.0 - with: - cosign-release: 'v2.4.0' - - # Workaround: https://github.com/docker/build-push-action/issues/461 - - name: Setup Docker buildx - uses: docker/setup-buildx-action@v4 - - - name: Log into registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@v6 - with: - images: "ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}" - - - name: Build and push main Docker image - id: build-and-push-main - if: ${{ inputs.main_image }} - uses: docker/build-push-action@v7 - with: - context: ${{ inputs.context_path }} - file: ${{ inputs.dockerfile_path }} - push: ${{ github.event_name != 'pull_request' }} - build-args: ${{ inputs.build_args || '' }} - tags: | - ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }} - ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}:${{ inputs.version }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --yes to cosign below. - # https://github.com/sigstore/cosign - - name: Sign the published Docker image - if: ${{ inputs.main_image && github.event_name != 'pull_request' }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign --yes {}@${{ steps.build-and-push-main.outputs.digest }} - - - name: Build and push specific Docker image - id: build-and-push-spec - uses: docker/build-push-action@v7 - with: - context: ${{ inputs.context_path }} - file: ${{ inputs.dockerfile_path }} - push: ${{ github.event_name != 'pull_request' }} - build-args: ${{ inputs.build_args || '' }} - tags: | - ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}:${{ inputs.version }}-${{ inputs.os_version }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --yes to cosign below. - # https://github.com/sigstore/cosign - - name: Sign the published Docker image - if: ${{ github.event_name != 'pull_request' }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign --yes {}@${{ steps.build-and-push-spec.outputs.digest }} diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index d6f9e53..9f529f5 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -4,6 +4,9 @@ on: pull_request: branches: [ master, testing ] +permissions: + contents: read + jobs: shellcheck: name: ShellCheck diff --git a/.grype.yaml b/.grype.yaml index 684e38e..23614b6 100644 --- a/.grype.yaml +++ b/.grype.yaml @@ -1,9 +1,9 @@ ignore: - vulnerability: CVE-2025-27558 # Not able to fix it at the moment # False positive: Grype's binary classifier reads the PHP interpreter's own version - # string (PHP 8.3.32 — embedded in /usr/local/bin/php, libphp.so, and the bundled - # extensions) as a "curl" binary at 8.3.32, then flags these curl CVEs (all fixed in - # curl 8.21.0). The REAL curl is the Debian package, patched (8.14.1-2+deb13u4) and + # string (embedded in /usr/local/bin/php, libphp.so, and the bundled extensions such + # as curl.so) as a "curl" binary, then flags these curl CVEs (all fixed in curl + # 8.21.0). The REAL curl is the Debian package, patched (8.14.1-2+deb13u4) and # correctly not flagged. Scoped per-CVE to binary-classified curl so a genuine future # curl finding still surfaces instead of being blanket-ignored. - vulnerability: CVE-2026-11856 diff --git a/CLAUDE.md b/CLAUDE.md index 0754506..6226b7d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ Both images use multi-stage builds (Alpine curl downloader stage → main stage) # Full-stack (build args: ubuntu_version_code=jammy|noble) docker build -t simplerisk/simplerisk simplerisk/ -# Minimal (build args: php_version=8.3|8.4|8.5) +# Minimal (build args: php_version=8.3|8.4|8.5; default 8.5) docker build -t simplerisk/simplerisk-minimal simplerisk-minimal/ ``` @@ -48,11 +48,14 @@ shellcheck simplerisk/entrypoint.sh simplerisk-minimal/entrypoint.sh make update_version VERSION=YYYYMMDD-NNN ``` -This runs four scripts in sequence: -1. `update_workflows.sh` — patches version in push workflow files -2. `simplerisk/generate_dockerfile.sh` — regenerates the full-stack Dockerfile from a template -3. `simplerisk-minimal/update_stack_and_workflows.sh` — regenerates `stack.yml` (with a fresh random password) and updates workflow files -4. `simplerisk-minimal/generate_dockerfile.sh` — regenerates the minimal Dockerfile +This runs three scripts in sequence: +1. `simplerisk/generate_dockerfile.sh` — regenerates the full-stack Dockerfile from a template +2. `simplerisk-minimal/update_stack_and_workflows.sh` — regenerates `stack.yml` +3. `simplerisk-minimal/generate_dockerfile.sh` — regenerates the minimal Dockerfile + +The version now lives only in the two generated Dockerfiles (`ENV version=`), which is +where `promote-latest.yml` and `create_new_tag.yml` read it from. No workflow carries a +version pin any more. ### Nix dev environment @@ -118,8 +121,9 @@ The entrypoint script handles: ### CI/CD -- **PRs** trigger `container-validation.yml`: builds all 4 variants (jammy, noble, php83, php84, php85), runs Dockle (Dockerfile linter) and Grype (CVE scanner, severity cutoff: critical, only-fixed). -- **Pushes** trigger separate workflows to publish to Docker Hub and GitHub Container Registry (GHCR). GHCR images are signed with Cosign/sigstore. The `simplerisk-minimal` push builds target both `linux/amd64` and `linux/arm64`. +- **PRs** trigger `container-validation.yml`: builds all 5 variants (jammy, noble, php83, php84, php85), runs Dockle (Dockerfile linter) and Grype (CVE scanner, severity cutoff: critical, only-fixed), and runs `generator_checks` — the two `test_generate_dockerfile.sh` harnesses that pin the generators' version/source-mode behaviour. +- **Release images are built once, then promoted — never rebuilt.** A push to `testing` runs `publish-testing.yml`, which builds both images from the current testing bundle and publishes immutable tags: `simplerisk-minimal` gets `-php83/-php84/-php85` (multi-arch `linux/amd64,linux/arm64`) and `simplerisk` gets `-jammy/-noble` (amd64). Each image's default variant also takes the bare `` and the floating `:testing`. +- **GA is a manual promote, not a build.** After the release merges to `master`, dispatch `promote-latest.yml`. It retags Docker Hub `:latest` to the existing RC digest (`buildx imagetools create`, multi-arch preserved), mirrors the same digests to GHCR cosign-signed, and writes SSM `/simplerisk/customers/image-tag/latest`. Nothing is rebuilt, so the bytes validated in testing are the bytes that ship. A currency guard refuses to promote a version whose digest is not the one `:testing` currently points at. - The reusable workflow files (`*_rw.yml`) are called by the entry-point workflows. ### Vulnerability ignore list diff --git a/Makefile b/Makefile index c2f6fac..5eda093 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,6 @@ help: @echo -e "$$HELP_MESSAGE" update_version: - ./update_workflows.sh $(VERSION) ./simplerisk/generate_dockerfile.sh $(VERSION) ./simplerisk-minimal/update_stack_and_workflows.sh $(VERSION) ./simplerisk-minimal/generate_dockerfile.sh $(VERSION) diff --git a/gorin.toml b/gorin.toml index 42b630f..c8cb283 100644 --- a/gorin.toml +++ b/gorin.toml @@ -10,8 +10,7 @@ check_dependencies [options.update_version] description = "Updates the version of Dockerfiles, stacks and others" command = """ -\n\t./update_workflows.sh $(VERSION) -\t./simplerisk/generate_dockerfile.sh $(VERSION) +\n\t./simplerisk/generate_dockerfile.sh $(VERSION) \t./simplerisk-minimal/update_stack_and_workflows.sh $(VERSION) \t./simplerisk-minimal/generate_dockerfile.sh $(VERSION) """ diff --git a/simplerisk-minimal/Dockerfile b/simplerisk-minimal/Dockerfile index 0a2aaea..0554029 100644 --- a/simplerisk-minimal/Dockerfile +++ b/simplerisk-minimal/Dockerfile @@ -16,13 +16,13 @@ SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] # updates feed, then extract -- fail-closed unless PREGA_BUNDLE_FALLBACK allows the # pre-GA path. See common/download_and_verify_bundle.sh. COPY common/download_and_verify_bundle.sh /download_and_verify_bundle.sh -RUN PREGA_BUNDLE_FALLBACK="$PREGA_BUNDLE_FALLBACK" sh /download_and_verify_bundle.sh 20260519-001 +RUN PREGA_BUNDLE_FALLBACK="$PREGA_BUNDLE_FALLBACK" sh /download_and_verify_bundle.sh 20260820-001 FROM php:${php_version}-apache LABEL maintainer="SimpleRisk " -ENV version=20260519-001 +ENV version=20260820-001 WORKDIR /var/www diff --git a/simplerisk-minimal/stack.yml b/simplerisk-minimal/stack.yml index d3ea61e..765dde6 100644 --- a/simplerisk-minimal/stack.yml +++ b/simplerisk-minimal/stack.yml @@ -8,7 +8,7 @@ services: - DB_SETUP=automatic - DB_SETUP_PASS=simplerisk_setup - SIMPLERISK_DB_HOSTNAME=mysql - image: simplerisk/simplerisk-minimal:20260519-001 + image: simplerisk/simplerisk-minimal:20260820-001 ports: - "80:80" - "443:443" diff --git a/simplerisk/Dockerfile b/simplerisk/Dockerfile index d9fae79..bcaa9ba 100644 --- a/simplerisk/Dockerfile +++ b/simplerisk/Dockerfile @@ -14,13 +14,13 @@ SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] # -fsSL on the SQL fetch too: without --fail, curl writes the 404 body into # /simplerisk.sql and the image ships an HTML error page as its schema. COPY common/download_and_verify_bundle.sh /download_and_verify_bundle.sh -RUN PREGA_BUNDLE_FALLBACK="$PREGA_BUNDLE_FALLBACK" sh /download_and_verify_bundle.sh 20260519-001 && \ - curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260519-001.sql" > /simplerisk.sql +RUN PREGA_BUNDLE_FALLBACK="$PREGA_BUNDLE_FALLBACK" sh /download_and_verify_bundle.sh 20260820-001 && \ + curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260820-001.sql" > /simplerisk.sql # Using Ubuntu image FROM ubuntu:${ubuntu_version_code} -ENV version=20260519-001 +ENV version=20260820-001 # Maintained by SimpleRisk LABEL maintainer="Simplerisk " diff --git a/update_workflows.sh b/update_workflows.sh deleted file mode 100755 index 34e3552..0000000 --- a/update_workflows.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -SCRIPT_LOCATION="$(dirname "$(readlink -f "$0")")" -readonly SCRIPT_LOCATION - -[ -z "${1:-}" ] && echo "No release version provided. Aborting." && exit 1 || release=$1 - -for workflow in "$SCRIPT_LOCATION"/.github/workflows/push*; do - sed -i -r "s/(version:) \"[0-9]{8,}-[0-9]{3,}\"/\1 \"${release}\"/g" "$workflow" -done