From 66a5f99bfaa95fb42e28ac390a258207972621ea Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 22 May 2026 09:45:38 +0000 Subject: [PATCH] gh: pre-pull released tailscale images for fork-PR CI Fork PRs anonymous-pull tailscale/tailscale:vX.Y at test time and hit Docker Hub rate limits, causing flakes. A new build-tailscale-released job pulls the MustTestVersions set from ghcr.io once per CI run and ships them to every test job as a gzipped tarball artifact, matching the shape of the existing headscale/HEAD/postgres caches. The version list is driven by 'hi list-versions' so capver is the single source of truth; adding a version there propagates to CI without YAML edits. ghcr.io public reads need no auth, so the new job has no docker login step. --- .../workflows/integration-test-template.yml | 6 ++ .github/workflows/test-integration.yaml | 55 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-test-template.yml b/.github/workflows/integration-test-template.yml index 427f63e2..68dbfd36 100644 --- a/.github/workflows/integration-test-template.yml +++ b/.github/workflows/integration-test-template.yml @@ -51,6 +51,11 @@ jobs: with: name: tailscale-head-image path: /tmp/artifacts + - name: Download tailscale released images + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: tailscale-released-images + path: /tmp/artifacts - name: Download hi binary uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: @@ -86,6 +91,7 @@ jobs: run: | gunzip -c /tmp/artifacts/headscale-image.tar.gz | docker load gunzip -c /tmp/artifacts/tailscale-head-image.tar.gz | docker load + gunzip -c /tmp/artifacts/tailscale-released-images.tar.gz | docker load if [ -f /tmp/artifacts/postgres-image.tar.gz ]; then gunzip -c /tmp/artifacts/postgres-image.tar.gz | docker load fi diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index f257ae0e..e99cafcc 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -9,6 +9,9 @@ concurrency: jobs: # build: Builds binaries and Docker images once, uploads as artifacts for reuse. # build-postgres: Pulls postgres image separately to avoid Docker Hub rate limits. + # build-tailscale-released: Pre-pulls released Tailscale images from ghcr.io + # so fork PRs (no DOCKERHUB_USERNAME secret) don't hit Docker Hub rate + # limits at test time. # sqlite: Runs all integration tests with SQLite backend. # postgres: Runs a subset of tests with PostgreSQL to verify database compatibility. build: @@ -150,9 +153,57 @@ jobs: name: postgres-image path: postgres-image.tar.gz retention-days: 10 - sqlite: + build-tailscale-released: + runs-on: ubuntu-24.04-arm needs: build if: needs.build.outputs.files-changed == 'true' + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 + - uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3 + with: + primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} + restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }} + - name: Force overlay2 storage driver + run: | + sudo mkdir -p /etc/docker + echo '{"storage-driver":"overlay2"}' | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker + docker version + - name: List Tailscale versions to pre-pull + id: versions + run: | + versions=$(nix develop --command go run ./cmd/hi list-versions --set=must --exclude=head) + echo "versions=${versions}" >> "$GITHUB_OUTPUT" + echo "Pre-pulling: ${versions}" + - name: Pull released Tailscale images + run: | + # ghcr.io public reads are anonymous and unmetered, so no docker + # login is needed even on fork PRs without DOCKERHUB_USERNAME. + # Pull in parallel; xargs -P 0 fans out one process per tag and + # returns non-zero if any pull fails. + echo "${{ steps.versions.outputs.versions }}" \ + | tr ' ' '\n' \ + | xargs -P 0 -I{} docker pull "ghcr.io/tailscale/tailscale:{}" + - name: Save Tailscale images to tarball + run: | + # Single docker save with all refs: one consistent snapshot, no + # parallel-daemon race. + refs="" + for v in ${{ steps.versions.outputs.versions }}; do + refs="${refs} ghcr.io/tailscale/tailscale:${v}" + done + docker save ${refs} | gzip > tailscale-released-images.tar.gz + ls -lh tailscale-released-images.tar.gz + - name: Upload Tailscale released images + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: tailscale-released-images + path: tailscale-released-images.tar.gz + retention-days: 10 + sqlite: + needs: [build, build-tailscale-released] + if: needs.build.outputs.files-changed == 'true' strategy: fail-fast: false matrix: @@ -309,7 +360,7 @@ jobs: postgres_flag: "--postgres=0" database_name: "sqlite" postgres: - needs: [build, build-postgres] + needs: [build, build-postgres, build-tailscale-released] if: needs.build.outputs.files-changed == 'true' strategy: fail-fast: false