From 57be4d409f03f822cd6e2df71fbe0f4a009bd4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Fei?= Date: Thu, 5 Sep 2024 16:48:18 +0200 Subject: [PATCH] feat: immutable digest for the most specific tags (#113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect updates of the Dockerfile template and Barman python dependencies, and increase the imageReleaseVersion accordingly. Avoid pushing an image if its most specific tag (fullTag) already exists, thus preventing the override of the existing digest. Signed-off-by: Niccolò Fei --- .github/workflows/build.yml | 32 +++++++++++++++++++++++++++++++- lib/repo_funcs.sh | 30 ++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc2dd802..832e3cf2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,8 +72,29 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # When publishing new images from main, we should not overwrite an existing + # tag in order to guarantee the tag's SHA digest consistency. + - name: Verify primary tag is not overwritten + run: | + echo "MISSING_TAG=false" >> $GITHUB_ENV + # if we are not on the main branch, always push + if [ "${GITHUB_REF#refs/heads/}" != main ]; then + echo "MISSING_TAG=true" >> $GITHUB_ENV + exit 0 + fi + IMAGE="${IMAGE_RELEASE}:${{ matrix.fullTag }}" + # If the primary tag already exists, skip the building phase + if skopeo inspect docker://${IMAGE} >/dev/null 2>/dev/null; then + echo "Image ${IMAGE} already exists" + # We still need to grab the digest to build the imageCatalog + echo "OLD_DIGEST=$(skopeo inspect docker://${IMAGE} --format '{{ .Digest }}')" >> $GITHUB_ENV + else + echo "MISSING_TAG=true" >> $GITHUB_ENV + fi + - name: Build and load uses: docker/build-push-action@v6 + if: ${{ env.MISSING_TAG == 'true' }} with: context: ${{ matrix.dir }} file: ${{ matrix.file }} @@ -83,6 +104,7 @@ jobs: - name: Dockle scan uses: erzz/dockle-action@v1 + if: ${{ env.MISSING_TAG == 'true' }} with: image: "${{ env.IMAGE_STAGING }}:${{ matrix.tags[0] }}" exit-code: '1' @@ -92,6 +114,7 @@ jobs: - name: Run Snyk to check Docker image for vulnerabilities uses: snyk/actions/docker@master + if: ${{ env.MISSING_TAG == 'true' }} continue-on-error: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} @@ -101,6 +124,7 @@ jobs: - name: Upload result to GitHub Code Scanning uses: github/codeql-action/upload-sarif@v3 + if: ${{ env.MISSING_TAG == 'true' }} continue-on-error: true with: sarif_file: snyk.sarif @@ -108,6 +132,7 @@ jobs: - name: Build and push id: build uses: docker/build-push-action@v6 + if: ${{ env.MISSING_TAG == 'true' }} with: context: ${{ matrix.dir }} file: ${{ matrix.file }} @@ -123,7 +148,12 @@ jobs: BASE_IMAGE=${IMAGE_RELEASE} fi - IMAGE=${BASE_IMAGE}:${{ matrix.fullTag }}@${{ steps.build.outputs.digest }} \ + DIGEST="${{ steps.build.outputs.digest }}" + if [[ "${{ env.MISSING_TAG }}" == "false" ]]; then + DIGEST="${{ env.OLD_DIGEST }}" + fi + + IMAGE=${BASE_IMAGE}:${{ matrix.fullTag }}@${DIGEST} \ MAJOR=${{ matrix.version }} \ yq --null-input '{ "apiVersion": "postgresql.cnpg.io/v1", diff --git a/lib/repo_funcs.sh b/lib/repo_funcs.sh index a22e4434..22ac9420 100644 --- a/lib/repo_funcs.sh +++ b/lib/repo_funcs.sh @@ -90,6 +90,14 @@ generate_postgres() { pipOptions="--break-system-packages" fi + dockerTemplate="Dockerfile.template" + if [[ ${version} -gt "${POSTGRESQL_LATEST_MAJOR_RELEASE}" ]]; then + dockerTemplate="Dockerfile-beta.template" + fi + + # Update requirements.txt + echo "$requirements" > "$versionDir/requirements.txt" + # Output the image being updated echo "$postgresImageVersion" @@ -111,9 +119,9 @@ generate_postgres() { newRelease="false" - # Detect if postgres image updated + # Detect an update of the postgres image if [ "$oldPostgresImageLastUpdate" != "$postgresImageLastUpdate" ]; then - echo "Debian Image changed from $oldPostgresImageLastUpdate to $postgresImageLastUpdate" + echo "Postgres image timestamp changed from $oldPostgresImageLastUpdate to $postgresImageLastUpdate" newRelease="true" record_version "${versionFile}" "POSTGRES_IMAGE_LAST_UPDATED" "${postgresImageLastUpdate}" fi @@ -125,6 +133,18 @@ generate_postgres() { record_version "${versionFile}" "BARMAN_VERSION" "${barmanVersion}" fi + # Detect an update of Dockerfile template + if [[ -n $(git diff --name-status "$dockerTemplate") ]]; then + echo "Detected update of $dockerTemplate" + newRelease="true" + fi + + # Detect an update of requirements.txt + if [[ -n $(git diff --name-status "$versionDir/requirements.txt") ]]; then + echo "Detected update of requirements.txt dependencies" + newRelease="true" + fi + if [ "$oldPostgresImageVersion" != "$postgresImageVersion" ]; then echo "PostgreSQL base image changed from $oldPostgresImageVersion to $postgresImageVersion" record_version "${versionFile}" "IMAGE_RELEASE_VERSION" 1 @@ -135,12 +155,6 @@ generate_postgres() { record_version "${versionFile}" "IMAGE_RELEASE_VERSION" $imageReleaseVersion fi - dockerTemplate="Dockerfile.template" - if [[ ${version} -gt "${POSTGRESQL_LATEST_MAJOR_RELEASE}" ]]; then - dockerTemplate="Dockerfile-beta.template" - fi - - echo "$requirements" > "$versionDir/requirements.txt" sed -e 's/%%POSTGRES_IMAGE_VERSION%%/'"$postgresImageVersion"'/g' \ -e 's/%%IMAGE_RELEASE_VERSION%%/'"$imageReleaseVersion"'/g' \ -e 's/%%PIP_OPTIONS%%/'"${pipOptions}"'/g' \