From 579551abe43cbf96b68fef2cf573e2287661dc18 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Fri, 19 Jun 2026 13:24:41 +0200 Subject: [PATCH 1/2] Only trigger build if files affecting the build changed Add a path filter to the workflow trigger that is equivalent to the configuration in the .dockerignore file. Since pushing tags should trigger a build anyway, we need two workflows, each with different trigger settings. This allows us to dissolve the Prepare step from the build workflow because the parameters it computed can now be provided as inputs. Signed-off-by: Tom Wieczorek --- .dockerignore | 2 + .github/workflows/build.yaml | 95 ++++++++++++++++------------------ .github/workflows/ci.yaml | 25 +++++++++ .github/workflows/release.yaml | 19 +++++++ .github/workflows/scan.yaml | 27 +++------- 5 files changed, 96 insertions(+), 72 deletions(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.dockerignore b/.dockerignore index d88a5ec..6ffc39e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,5 @@ .* * + +# Keep in sync with the paths in .github/workflows/ci.yaml. !src/ diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 31ce63c..5938e9d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,40 +1,42 @@ name: Build on: - pull_request: - branches: - - main - push: - branches: - - main - tags: - - v?* + workflow_call: + inputs: + registry: + type: string + required: true + description: The registry to push the image to. + image-owner: + type: string + required: true + description: The owner of the image. + image-name: + type: string + required: true + description: The name of the image. + image-tag: + type: string + required: true + description: The tag of the image. + secrets: + registry-username: + description: The username for the registry. + registry-password: + description: The password for the registry. jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Prepare - id: prep - run: | - # If this is a git tag, use the tag name as a docker tag - if [[ $GITHUB_REF == refs/tags/v?* ]]; then - registry=quay.io - name="$GITHUB_REPOSITORY" - tag="${GITHUB_REF#refs/tags/v}" - tag="${tag//+/-}" - else - registry=ttl.sh - name="${GITHUB_REPOSITORY//\//-}-$GITHUB_SHA-$GITHUB_RUN_ID" - tag=1d - fi - - { - echo registry="$registry" - echo name="$name" - echo tag="$tag" - } >>"$GITHUB_OUTPUT" + id: prepare + env: + # GitHub Actions doesn't seem to support the secrets + # context in if fields, hence this workaround. + REGISTRY_CREDENTIALS_AVAILABLE: "${{ case(secrets.registry-username != '', 'true', '') }}" + run: echo registry-credentials-available="$REGISTRY_CREDENTIALS_AVAILABLE" >>"$GITHUB_OUTPUT" - name: Set up QEMU uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 @@ -51,28 +53,21 @@ jobs: with: builder: ${{ steps.buildx.outputs.name }} platforms: linux/amd64,linux/arm64,linux/arm,linux/riscv64 - tags: ${{ format('{0}/{1}:{2}', steps.prep.outputs.registry, steps.prep.outputs.name, steps.prep.outputs.tag) }} + tags: ${{ format('{0}/{1}/{2}:{3}', inputs.registry, inputs.image-owner, inputs.image-name, inputs.image-tag) }} outputs: type=oci,dest=oci-image.tar - - name: Compress OCI image archive - # Pre-compress the image archive so that the upload-artifact action - # doesn't try to do it. The layers inside the tar archive are themselves - # already gzip compressed, so this is not for size reduction, but solely - # to prevent the very slow compression process in the upload-artifact - # action. - # See: https://github.com/actions/upload-artifact/issues/199 - # See: https://github.com/actions/toolkit/blob/6c1f9eaae833355a0b212b66c5f2e3ac366de185/packages/artifact/src/internal/upload-gzip.ts#L11-L33 - # Might be fixed when upload-artifact@v4 gets released: https://github.com/actions/toolkit/pull/1488 - run: zstdmt --fast oci-image.tar - - name: Upload OCI image archive uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: oci-image.tar.zst - path: oci-image.tar.zst + name: ${{ format('{0}-{1}.tar', inputs.image-name, inputs.image-tag) }} + path: oci-image.tar + # The layers inside the tar archive are themselves already gzip + # compressed. No need to compress the metadata. Speeds things up a + # bit. + compression-level: 0 - name: Extract OCI image archive - run: mkdir image && tar xf oci-image.tar.zst -C image/ + run: mkdir image && tar xf oci-image.tar -C image/ - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 @@ -83,18 +78,16 @@ jobs: exit-code: "1" - name: Log in to registry - if: steps.prep.outputs.registry != 'ttl.sh' + if: steps.prepare.outputs.registry-credentials-available uses: redhat-actions/podman-login@4934294ad0449894bcd1e9f191899d7292469603 # v1.7 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - registry: ${{ steps.prep.outputs.registry }} + registry: ${{ inputs.registry }} + username: ${{ secrets.registry-username }} + password: ${{ secrets.registry-password }} - name: Upload OCI image to registry env: - REGISTRY: ${{ steps.prep.outputs.registry }} - NAME: ${{ steps.prep.outputs.name }} - TAG: ${{ steps.prep.outputs.tag }} + IMAGE: ${{ format('{0}/{1}/{2}:{3}', inputs.registry, inputs.image-owner, inputs.image-name, inputs.image-tag) }} run: | podmanArgs=(-v "$(realpath oci-image.tar):/image.tar:ro") skopeoArgs=(--multi-arch all --preserve-digests) @@ -107,4 +100,4 @@ jobs: set -x podman run "${podmanArgs[@]}" \ docker://quay.io/skopeo/stable:v1.18.0 copy "${skopeoArgs[@]}" \ - oci-archive:/image.tar "docker://$REGISTRY/$NAME:$TAG" + oci-archive:/image.tar "docker://$IMAGE" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6c22a67 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,25 @@ +name: CI + +on: + pull_request: &triggers + branches: + - main + paths: + - .github/workflows/ci.yaml + - .github/workflows/build.yaml + - Dockerfile + - .dockerignore + # keep in sync with .dockerignore + - src/** + push: *triggers + +jobs: + build: + name: Build OCI image + uses: ./.github/workflows/build.yaml + with: + # This will produce images tagged like ttl.sh/k0sproject/cni-node-27741332811-1:1d. + registry: ttl.sh + image-owner: ${{ github.repository_owner }} + image-name: ${{ format('{0}-{1}-{2}', github.event.repository.name, github.run_id, github.run_attempt) }} + image-tag: 1d diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9cabb9f --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,19 @@ +name: Release + +on: + push: + tags: + - v?* + +jobs: + build: + name: Build OCI image + uses: ./.github/workflows/build.yaml + with: + registry: quay.io + image-owner: ${{ github.repository_owner }} + image-name: ${{ github.event.repository.name }} + image-tag: ${{ github.ref_name }} + secrets: + registry-username: ${{ secrets.DOCKER_USERNAME }} + registry-password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml index 7808e77..14bb09a 100644 --- a/.github/workflows/scan.yaml +++ b/.github/workflows/scan.yaml @@ -14,28 +14,13 @@ jobs: security-events: write # upload sarif results steps: - - name: Prepare - id: prep + - name: Determine latest release + id: latest-release + if: github.event.release.tag_name == '' env: - PUBLISHED_TAG: ${{ github.event.release.tag_name }} GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} - run: | - if [ -n "$PUBLISHED_TAG" ]; then - gitTag="$PUBLISHED_TAG" - else - # Get the tag name from the latest GitHub release - gitTag="$(gh api repos/{owner}/{repo}/releases/latest --jq .tag_name)" - fi - - # Determine OCI image tag - imageTag="${gitTag//+/-}" - imageTag="${imageTag#v}" - - { - echo git-tag="$gitTag" - echo image-tag="$imageTag" - } >>"$GITHUB_OUTPUT" + run: gh api 'repos/{owner}/{repo}/releases/latest' --jq '"tag-name=\(.tag_name)"' >>"$GITHUB_OUTPUT" # The checkout is solely required for the upload-sarif action. It needs to # know the ref and sha. Providing those manually won't work, for unknown @@ -43,13 +28,13 @@ jobs: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: - ref: ${{ format('refs/tags/{0}', steps.prep.outputs.git-tag) }} + ref: ${{ format('refs/tags/{0}', github.event.release.tag_name || steps.latest-release.outputs.tag-name) }} persist-credentials: false - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: - image-ref: ${{ format('quay.io/{0}:{1}', github.repository, steps.prep.outputs.image-tag) }} + image-ref: ${{ format('quay.io/{0}:{1}', github.repository, github.event.release.tag_name || steps.latest-release.outputs.tag-name) }} format: sarif output: trivy-results.sarif From b821da4e0e34e361f745abd113de8848f7461d93 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Fri, 19 Jun 2026 12:30:56 +0200 Subject: [PATCH 2/2] Add Renovate configuration The GitHub Actions workflow is still missing, but everything else is setup, mostly how the k0s repo is doing it, stripped down and adapted to the needs of this repo: * Do checkouts based on git commits. The git archive output is not guaranteed to be stable across git versions, so it might change, even if the extracted contents are identical. Use Renovate to bump both the version and the commit. * Manage GitHub Action version bumps, no need for Dependabot. * Pin OCI images using their digest. * Add some special handling for the -alpineX.Y tag suffix. Signed-off-by: Tom Wieczorek --- Dockerfile | 22 ++++++++---------- renovate.json | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 renovate.json diff --git a/Dockerfile b/Dockerfile index d06b9dd..de30e9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,21 @@ -ARG \ - ALPINE_IMAGE=docker.io/library/alpine:3.24.1 \ - GOLANG_IMAGE=docker.io/library/golang:1.26.4-alpine3.24 \ - VERSION=1.9.1 \ - HASH=34bd82d47e981940751619c9cc44c095bb90bfcaf8d71865cbb822c37690a764 +ARG ALPINE_IMAGE=docker.io/library/alpine:3.24.1 +ARG GOLANG_IMAGE=docker.io/library/golang:1.26.4-alpine3.24 +# renovate: datasource=github-tags depName=containernetworking/plugins +ARG VERSION=1.9.1 COMMIT=adc3e6b5b581638afbd194cf2e9319ecbb0151a1 FROM --platform=$BUILDPLATFORM $GOLANG_IMAGE AS sources +RUN apk add git ENV GOTOOLCHAIN=local GOTELEMETRY=off WORKDIR /go/src/containernetworking-plugins -ARG VERSION HASH +ARG COMMIT RUN --mount=type=tmpfs,target=/tmp \ set -euo pipefail \ - && wget -qO /tmp/sources.tar.gz https://github.com/containernetworking/plugins/archive/refs/tags/v$VERSION.tar.gz \ - && { echo $HASH /tmp/sources.tar.gz | sha256sum -c -; } || { sha256sum /tmp/sources.tar.gz; exit 1; } \ - && tar xf /tmp/sources.tar.gz --strip-components=1 - + && git clone --bare --revision "$COMMIT" --depth 1 -c advice.detachedHead=false \ + https://github.com/containernetworking/plugins.git /tmp/clone \ + && git -C /tmp/clone archive --worktree-attributes "$COMMIT" | tar x FROM sources AS bins -ARG TARGETARCH SOURCE_DATE_EPOCH +ARG TARGETARCH SOURCE_DATE_EPOCH VERSION RUN --mount=type=cache,id=calico-gocache-$TARGETARCH,target=/root/.cache/go-build \ --network=none \ CGO_ENABLED=0 GOARCH=$TARGETARCH ./build_linux.sh -trimpath -buildvcs=false \ @@ -37,7 +36,6 @@ FROM scratch LABEL org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.source="https://github.com/k0sproject/cni-node" COPY --from=baselayout / / -ARG VERSION COPY --from=bins /go/src/containernetworking-plugins/bin/ /opt/cni/bin/ ENTRYPOINT ["/bin/cni-node"] CMD ["install"] diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..b7d1ab4 --- /dev/null +++ b/renovate.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + "docker:pinDigests", + ":configMigration", + ":gitSignOff", + ":semanticCommitsDisabled" + ], + "gitAuthor": "k0s-bot <110385897+k0s-bot@users.noreply.github.com>", + "labels": [ + "dependencies" + ], + "prTitleStrict": true, + "commitMessageAction": "Bump", + "commitMessageTopic": "{{depName}}", + "groupSingleUpdates": false, + "vulnerabilityAlerts": { + "enabled": false + }, + "enabledManagers": [ + "github-actions", + "dockerfile", + "custom.regex" + ], + "packageRules": [ + { + "description": "No digest-only bumps", + "matchUpdateTypes": [ + "digest" + ], + "enabled": false + }, + { + "description": "Bump the Alpine-based Go image, including the -alpineX.Y suffix", + "matchManagers": [ + "dockerfile" + ], + "matchPackageNames": [ + "docker.io/library/golang" + ], + "versioning": "regex:^(?\\d+)\\.(?\\d+)\\.(?\\d+)-alpine(?\\d+)\\.(?\\d+)$" + } + ], + "customManagers": [ + { + "customType": "regex", + "description": "Generic version bumps based on comments", + "managerFilePatterns": [ + "**/Dockerfile*", + "**/*.yaml", + "**/*.yml" + ], + "matchStrings": [ + "# renovate: datasource=(?\\S+)( registryUrl=(?.+))? depName=(?\\S+)( versioning=(?.+))?\\n[^:=\\n]+[:=][ \\t]*[\"']?(?[^@\\s\"']+)(?:(@|[^:=\\n]+[:=][ \\t]*[\"']?)(?[0-9a-f]{7,}))?[\"']?[ \\t]*(:?#|\\n)", + "[=:][ \\t]*[\"']?(?\\S+?)[\"']?[ \\t]*# renovate: datasource=(?\\S+)( registryUrl=(?.+))? depName=(?\\S+)( versioning=(?.+))?\\n" + ], + "datasourceTemplate": "{{{datasource}}}", + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}", + "extractVersionTemplate": "^v?(?.+)" + } + ] +}