diff --git a/.github/workflows/buildwithbake.yaml b/.github/workflows/buildwithbake.yaml new file mode 100644 index 00000000..25f35069 --- /dev/null +++ b/.github/workflows/buildwithbake.yaml @@ -0,0 +1,146 @@ +name: Bake images + +on: + schedule: + - cron: 0 8 * * 1 + workflow_dispatch: + inputs: + environment: + type: choice + options: + - testing + - production + default: testing + description: "Choose the environment to bake the images for" + +jobs: + testbuild: + name: Build for testing + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + security-events: write + outputs: + metadata: ${{ steps.build.outputs.metadata }} + images: ${{ steps.images.outputs.images }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Log in to the GitHub Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: 'arm64' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build and push + uses: docker/bake-action@v6 + id: build + env: + # Set the environment variable to the value of the input. If the input is not set, we are running via cron, + # so default to production. + #environment: ${{ github.event.inputs.environment || 'production' }} + environment: testing + registry: ghcr.io/${{ github.repository_owner }} + revision: ${{ github.sha }} + with: + push: true + + - name: Generated images + id: images + run: | + echo "images=$(echo '${{steps.build.outputs.metadata}}' | jq -c '[ .[]."image.name" | sub(",.*";"" )]')" >> "$GITHUB_OUTPUT" + + security: + runs-on: ubuntu-latest + needs: + - testbuild + strategy: + matrix: + image: ${{fromJson(needs.testbuild.outputs.images)}} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Log in to the GitHub Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Dockle + uses: erzz/dockle-action@v1 + with: + image: ${{ matrix.image }} + exit-code: '1' + + - name: Snyk + uses: snyk/actions/docker@master + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: "${{ matrix.image }}" + args: --severity-threshold=high --file=Dockerfile + + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: snyk.sarif + + prodbuild: + if: github.event.inputs.environment == 'production' || github.event_name == 'schedule' + name: Build for production + runs-on: ubuntu-latest + needs: + - security + permissions: + contents: read + packages: write + security-events: write + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Log in to the GitHub Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: 'arm64' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build and push + uses: docker/bake-action@v6 + id: build + env: + # Set the environment variable to the value of the input. If the input is not set, we are running via cron, + # so default to production. + environment: production + registry: ghcr.io/${{ github.repository_owner }} + revision: ${{ github.sha }} + with: + push: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..3a77a829 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +ARG BASE=debian:bookworm-slim +FROM $BASE AS minimal + +ARG PG_VERSION +ARG PG_MAJOR=${PG_VERSION%%.*} + +ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin + +RUN apt-get update && \ + apt-get install -y --no-install-recommends postgresql-common ca-certificates gnupg && \ + /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \ + apt-get install --no-install-recommends -o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" postgresql-common -y && \ + sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf && \ + apt-get install --no-install-recommends \ + -o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" "postgresql-${PG_MAJOR}=${PG_VERSION}*" -y && \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \ + rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/* + + +RUN usermod -u 26 postgres +USER 26 + +FROM minimal AS standard + +LABEL org.opencontainers.image.title="CloudNativePG PostgreSQL $PG_VERSION standard" +LABEL org.opencontainers.image.description="A standard PostgreSQL $PG_VERSION container image, with a few extensions and all the locales" + +USER root +RUN apt-get update && \ + apt-get install -y --no-install-recommends locales-all \ + "postgresql-${PG_MAJOR}-pgaudit" \ + "postgresql-${PG_MAJOR}-pgvector" \ + "postgresql-${PG_MAJOR}-pg-failover-slots" && \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \ + rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/* + +USER 26 diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 00000000..8dfd26ee --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,104 @@ +variable "environment" { + default = "testing" + validation { + condition = contains(["testing", "production"], environment) + error_message = "environment must be either testing or production" + } +} + +variable "registry" { + default = "localhost:5000" +} + +// Use the revision variable to identify the commit that generated the image +variable "revision" { + default = "" +} + +fullname = ( environment == "testing") ? "${registry}/postgresql-testing" : "{registry}/postgresql" +now = timestamp() + +target "default" { + matrix = { + tgt = [ + "minimal", + // "standard" + ] + pgVersion = [ + // "13.18", + // "14.15", + // "15.10", + "16.6", + "17.2" + ] + base = [ + // renovate: datasource=docker versioning=loose + // "debian:bookworm-slim@sha256:d365f4920711a9074c4bcd178e8f457ee59250426441ab2a5f8106ed8fe948eb", + // renovate: datasource=docker versioning=loose + "debian:bullseye-slim@sha256:6344a6747740d465bff88e833e43ef881a8c4dd51950dba5b30664c93f74cbef" + ] + } + dockerfile = "Dockerfile" + name = "postgresql-${index(split(".",pgVersion),0)}-${distroVersion(base)}-${tgt}" + tags = [ + "${fullname}:${index(split(".",pgVersion),0)}-${distroVersion(base)}-${tgt}", + "${fullname}:${pgVersion}-${distroVersion(base)}-${tgt}", + "${fullname}:${pgVersion}-${formatdate("YYYYMMDDhhmm", now)}-${distroVersion(base)}-${tgt}" + ] + context = "." + target = "${tgt}" + args = { + PG_VERSION = "${pgVersion}" + BASE = "${base}" + } + attest = [ + "type=provenance,mode=max", + "type=sbom" + ] + annotations = [ + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.created=${now}", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.url=https://github.com/cloudnative-pg/postgres-containers", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.source=https://github.com/cloudnative-pg/postgres-containers", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.version=${pgVersion}", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.revision=${revision}", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.vendor=The CloudNativePG Contributors", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.title=CloudNativePG PostgreSQL ${pgVersion} ${tgt}", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.description=A ${tgt} PostgreSQL ${pgVersion} container image", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.documentation=https://github.com/cloudnative-pg/postgres-containers", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.authors=The CloudNativePG Contributors", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.licenses=Apache-2.0", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.base.name=docker.io/library/${tag(base)}", + "index,manifest,manifest-descriptor,index-descriptor:org.opencontainers.image.base.digest=${digest(base)}" + ] + labels = { + "org.opencontainers.image.created" = "${now}", + "org.opencontainers.image.url" = "https://github.com/cloudnative-pg/postgres-containers", + "org.opencontainers.image.source" = "https://github.com/cloudnative-pg/postgres-containers", + "org.opencontainers.image.version" = "${pgVersion}", + "org.opencontainers.image.revision" = "${revision}", + "org.opencontainers.image.vendor" = "The CloudNativePG Contributors", + "org.opencontainers.image.title" = "CloudNativePG PostgreSQL ${pgVersion} ${tgt}", + "org.opencontainers.image.description" = "A ${tgt} PostgreSQL ${pgVersion} container image", + "org.opencontainers.image.documentation" = "https://github.com/cloudnative-pg/postgres-containers", + "org.opencontainers.image.authors" = "The CloudNativePG Contributors", + "org.opencontainers.image.licenses" = "Apache-2.0" + "org.opencontainers.image.base.name" = "docker.io/library/debian:${tag(base)}" + "org.opencontainers.image.base.digest" = "${digest(base)}" + } + //platforms = ["linux/amd64", "linux/arm64"] +} + +function tag { + params = [ imageNameWithSha ] + result = index(split("@", index(split(":", imageNameWithSha), 1)), 0) +} + +function distroVersion { + params = [ imageNameWithSha ] + result = index(split("-", tag(imageNameWithSha)), 0) +} + +function digest { + params = [ imageNameWithSha ] + result = index(split("@", imageNameWithSha), 1) +} diff --git a/renovate.json5 b/renovate.json5 new file mode 100644 index 00000000..cd9f42da --- /dev/null +++ b/renovate.json5 @@ -0,0 +1,15 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", "docker:pinDigests"], + "enabledManagers": ["custom.regex"], + "customManagers": [ + { + "customType": "regex", + "fileMatch": ["docker-bake.hcl"], + "matchStrings": [ + "\\/\\/\\s*renovate:\\s*datasource=(?.*?)\\s+(versioning=(?.*?))?\\s+\"(?[^:]+):(?[^\\s@]+)(@(?sha256:[0-9a-f]*))?\"" + ], + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}" + }, + ], +}