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 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?(?.+)" + } + ] +}