From 4e5e538903b5b00728767d1a705fbf6c12031a9b Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Thu, 25 Jun 2026 08:07:29 +0000 Subject: [PATCH] ci: replace the integration workflow with the flake checks and a legacy pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI runs nix build .#checks..integration-* instead of the bespoke docker matrix — the generated checks are the matrix. The tests the hermetic VM can't run (real-internet DERP, docker cable-pull) run via cmd/hi in a slim legacy workflow, which also keeps cmd/hi and the integration Dockerfiles exercised. check-tests guards the pinned versions against capver drift. --- .github/workflows/check-tests.yaml | 18 +- .../workflows/integration-test-template.yml | 146 ------ .../workflows/test-integration-legacy.yaml | 75 +++ .github/workflows/test-integration.yaml | 433 ++---------------- 4 files changed, 132 insertions(+), 540 deletions(-) delete mode 100644 .github/workflows/integration-test-template.yml create mode 100644 .github/workflows/test-integration-legacy.yaml diff --git a/.github/workflows/check-tests.yaml b/.github/workflows/check-tests.yaml index b73011732..dd3637b22 100644 --- a/.github/workflows/check-tests.yaml +++ b/.github/workflows/check-tests.yaml @@ -37,9 +37,23 @@ jobs: if: steps.changed-files.outputs.files == 'true' run: | (cd .github/workflows && go generate) - git diff --exit-code .github/workflows/test-integration.yaml + git diff --exit-code integration/tests.nix integration/postgres-tests.nix integration/excluded-tests.json - name: Show missing tests if: failure() run: | - git diff .github/workflows/test-integration.yaml + git diff integration/tests.nix integration/postgres-tests.nix integration/excluded-tests.json + + # The pinned tailscale images must match the versions the suite requests + # (MustTestVersions); a capver bump that isn't mirrored here makes the + # offline integration checks fail at runtime. + - name: Check tailscale version pins match capver + if: steps.changed-files.outputs.files == 'true' + run: | + want=$(nix develop --command go run ./cmd/hi list-versions --set=must --exclude=head | tr ' ' '\n' | sort) + have=$(jq -r '.images | keys[]' integration/tailscale-versions.json | sort) + if [ "$want" != "$have" ]; then + echo "::error::integration/tailscale-versions.json is stale vs capver; run 'update-integration-images' in the dev shell" + diff <(echo "$want") <(echo "$have") || true + exit 1 + fi diff --git a/.github/workflows/integration-test-template.yml b/.github/workflows/integration-test-template.yml deleted file mode 100644 index 590185c96..000000000 --- a/.github/workflows/integration-test-template.yml +++ /dev/null @@ -1,146 +0,0 @@ -name: Integration Test Template - -on: - workflow_call: - inputs: - test: - required: true - type: string - postgres_flag: - required: false - type: string - default: "" - database_name: - required: true - type: string - -jobs: - test: - runs-on: ubuntu-24.04-arm - env: - # Github does not allow us to access secrets in pull requests, - # so this env var is used to check if we have the secret or not. - # If we have the secrets, meaning we are running on push in a fork, - # there might be secrets available for more debugging. - # If TS_OAUTH_CLIENT_ID and TS_OAUTH_SECRET is set, then the job - # will join a debug tailscale network, set up SSH and a tmux session. - # The SSH will be configured to use the SSH key of the Github user - # that triggered the build. - HAS_TAILSCALE_SECRET: ${{ secrets.TS_OAUTH_CLIENT_ID }} - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 2 - - name: Tailscale - if: ${{ env.HAS_TAILSCALE_SECRET }} - uses: tailscale/github-action@a392da0a182bba0e9613b6243ebd69529b1878aa # v4.1.0 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - tags: tag:gh - - name: Setup SSH server for Actor - if: ${{ env.HAS_TAILSCALE_SECRET }} - uses: alexellis/setup-sshd-actor@master - - name: Download headscale image - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - name: headscale-image - path: /tmp/artifacts - - name: Download tailscale HEAD image - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - 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: - name: hi-binary - path: /tmp/artifacts - - name: Download Go cache - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - name: go-cache - path: /tmp/artifacts - - name: Download postgres image - if: ${{ inputs.postgres_flag == '--postgres=1' }} - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - name: postgres-image - path: /tmp/artifacts - - 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: Load br_netfilter for in-cluster service routing - if: inputs.test == 'TestK8sOperator' - # TestK8sOperator runs k3s in a container; without br_netfilter on the - # host, bridged pod-to-pod traffic skips kube-proxy's ClusterIP DNAT and - # in-cluster DNS (kube-dns) is unreachable. The module cannot be loaded - # from inside the unprivileged-module rancher/k3s image, so load it here. - run: sudo modprobe br_netfilter - - name: Login to Docker Hub - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} - if: env.DOCKERHUB_USERNAME != '' - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - name: Load Docker images, Go cache, and prepare binary - 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 - chmod +x /tmp/artifacts/hi - docker images - # Extract Go cache to host directories for bind mounting - mkdir -p /tmp/go-cache - tar -xzf /tmp/artifacts/go-cache.tar.gz -C /tmp/go-cache - ls -la /tmp/go-cache/ /tmp/go-cache/.cache/ - - name: Run Integration Test - env: - HEADSCALE_INTEGRATION_HEADSCALE_IMAGE: headscale:${{ github.sha }} - HEADSCALE_INTEGRATION_TAILSCALE_IMAGE: tailscale-head:${{ github.sha }} - HEADSCALE_INTEGRATION_POSTGRES_IMAGE: ${{ inputs.postgres_flag == '--postgres=1' && format('postgres:{0}', github.sha) || '' }} - HEADSCALE_INTEGRATION_GO_CACHE: /tmp/go-cache/go - HEADSCALE_INTEGRATION_GO_BUILD_CACHE: /tmp/go-cache/.cache/go-build - # Mirror the docker/login-action secrets into env so the - # dockertestutil.Credentials resolver picks them up directly - # (otherwise it falls back to parsing ~/.docker/config.json, - # which works but is one step further from the source). - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} - run: /tmp/artifacts/hi run --stats --ts-memory-limit=300 --hs-memory-limit=1500 "^${{ inputs.test }}$" \ - --timeout=120m \ - ${{ inputs.postgres_flag }} - # Sanitize test name for artifact upload (replace invalid characters: " : < > | * ? \ / with -) - - name: Sanitize test name for artifacts - if: always() - id: sanitize - run: echo "name=${TEST_NAME//[\":<>|*?\\\/]/-}" >> $GITHUB_OUTPUT - env: - TEST_NAME: ${{ inputs.test }} - - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - if: always() - with: - name: ${{ inputs.database_name }}-${{ steps.sanitize.outputs.name }}-logs - path: "control_logs/*/*.log" - - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - if: always() - with: - name: ${{ inputs.database_name }}-${{ steps.sanitize.outputs.name }}-artifacts - path: control_logs/ - - name: Setup a blocking tmux session - if: ${{ env.HAS_TAILSCALE_SECRET }} - uses: alexellis/block-with-tmux-action@master diff --git a/.github/workflows/test-integration-legacy.yaml b/.github/workflows/test-integration-legacy.yaml new file mode 100644 index 000000000..5a56142e6 --- /dev/null +++ b/.github/workflows/test-integration-legacy.yaml @@ -0,0 +1,75 @@ +name: integration-legacy + +on: + pull_request: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +# A small number of integration tests cannot run as hermetic NixOS-VM checks +# because they need Tailscale's public DERP relays (real internet) — see +# excludedFromNixChecks in gh-action-integration-generator.go. They run here the +# legacy way, via cmd/hi against Dockerfile-built images, which also keeps the +# cmd/hi path and the integration Dockerfiles exercised in CI. The matrix is the +# generated integration/excluded-tests.json so this list has a single source. +jobs: + list: + runs-on: ubuntu-latest + outputs: + tests: ${{ steps.list.outputs.tests }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - id: list + run: echo "tests=$(jq -c . integration/excluded-tests.json)" >> "$GITHUB_OUTPUT" + + test: + needs: list + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test: ${{ fromJSON(needs.list.outputs.tests) }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: NixOS/nix-installer-action@6b8548fe06acfb0155a50ab5d561accb215764cc # main + - uses: Mic92/hestia/action@ff07bb902a9968ac0c3d0e51d90a606662a375d8 # main + + # Docker 29's default containerd snapshotter regresses these images; the + # classic overlay2 graph driver is the known-good path (mirrors the VM checks). + - 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 + + - name: Login to Docker Hub + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} + if: env.DOCKERHUB_USERNAME != '' + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + # cmd/hi requires prebuilt head images in CI (util.IsCI); released versions + # pull from the registry and tailscale-rs is built inline by tsric. + # Dockerfile.integration-ci COPYs a prebuilt headscale binary from the + # build context, so build it first (tailscale-HEAD builds from source). + - name: Build headscale + tailscale-HEAD images + run: | + nix develop --command bash -c 'CGO_ENABLED=0 GOOS=linux go build -o headscale ./cmd/headscale' + docker build --file Dockerfile.integration-ci --tag headscale:ci . + docker build --file Dockerfile.tailscale-HEAD --tag tailscale-head:ci . + + - name: Run ${{ matrix.test }} + env: + HEADSCALE_INTEGRATION_HEADSCALE_IMAGE: headscale:ci + HEADSCALE_INTEGRATION_TAILSCALE_IMAGE: tailscale-head:ci + run: nix develop --command go run ./cmd/hi run "^${{ matrix.test }}$" --timeout=900s diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 974e55233..3b8e858ce 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -1,406 +1,55 @@ name: integration -# To debug locally on a branch, and when needing secrets -# change this to include `push` so the build is ran on -# the main repository. -on: [pull_request] + +on: + pull_request: + branches: + - main + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true -defaults: - run: - shell: nix develop --fallback --command bash -e {0} + +permissions: + contents: read + +# Each test is a NixOS-VM flake check (full tailscale version matrix, sqlite +# plus a postgres variant) — one job per check, derived from the checks the +# flake exposes. The fileset-filtered, content-addressed checks hit the hestia +# cache when their inputs are unchanged. A prime job builds the shared images + +# test binary once so the matrix substitutes them instead of each rebuilding. 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: - runs-on: ubuntu-24.04-arm + list: + runs-on: ubuntu-latest outputs: - files-changed: ${{ steps.changed-files.outputs.files }} - steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - fetch-depth: 2 - - name: Get changed files - id: changed-files - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - with: - filters: | - files: - - '*.nix' - - 'go.*' - - '**/*.go' - - 'integration/**' - - 'config-example.yaml' - - '.github/workflows/test-integration.yaml' - - '.github/workflows/integration-test-template.yml' - - 'Dockerfile.*' - - uses: NixOS/nix-installer-action@6b8548fe06acfb0155a50ab5d561accb215764cc # main - if: steps.changed-files.outputs.files == 'true' - - uses: Mic92/hestia/action@ff07bb902a9968ac0c3d0e51d90a606662a375d8 # main - if: steps.changed-files.outputs.files == 'true' - - name: Build binaries and warm Go cache - if: steps.changed-files.outputs.files == 'true' - run: | - # Build all Go binaries in one nix shell to maximize cache reuse - go build -o hi ./cmd/hi - CGO_ENABLED=0 GOOS=linux go build -o headscale ./cmd/headscale - # Build integration test binary to warm the cache with all dependencies - go test -c ./integration -o /dev/null 2>/dev/null || true - - name: Upload hi binary - if: steps.changed-files.outputs.files == 'true' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: hi-binary - path: hi - retention-days: 10 - - name: Package Go cache - if: steps.changed-files.outputs.files == 'true' - run: | - # Package Go module cache and build cache - tar -czf go-cache.tar.gz -C ~ go .cache/go-build - - name: Upload Go cache - if: steps.changed-files.outputs.files == 'true' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: go-cache - path: go-cache.tar.gz - retention-days: 10 - - name: Force overlay2 storage driver - if: steps.changed-files.outputs.files == 'true' - run: | - # Docker 29 runner images default to overlayfs, which breaks - # docker build via Go SDK libraries and docker save/load - # tarball formats. overlay2 is the long-standing default. - # https://github.com/actions/runner-images/issues/13474 - sudo mkdir -p /etc/docker - echo '{"storage-driver":"overlay2"}' | sudo tee /etc/docker/daemon.json - sudo systemctl restart docker - docker version - - name: Login to Docker Hub - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} - if: env.DOCKERHUB_USERNAME != '' - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - name: Build headscale image - if: steps.changed-files.outputs.files == 'true' - run: | - docker build \ - --file Dockerfile.integration-ci \ - --tag headscale:${{ github.sha }} \ - . - docker save headscale:${{ github.sha }} | gzip > headscale-image.tar.gz - - name: Build tailscale HEAD image - if: steps.changed-files.outputs.files == 'true' - run: | - docker build \ - --file Dockerfile.tailscale-HEAD \ - --tag tailscale-head:${{ github.sha }} \ - . - docker save tailscale-head:${{ github.sha }} | gzip > tailscale-head-image.tar.gz - - name: Upload headscale image - if: steps.changed-files.outputs.files == 'true' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: headscale-image - path: headscale-image.tar.gz - retention-days: 10 - - name: Upload tailscale HEAD image - if: steps.changed-files.outputs.files == 'true' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: tailscale-head-image - path: tailscale-head-image.tar.gz - retention-days: 10 - build-postgres: - runs-on: ubuntu-24.04-arm - needs: build - if: needs.build.outputs.files-changed == 'true' - steps: - - name: Force overlay2 storage driver - shell: bash - run: | - sudo mkdir -p /etc/docker - echo '{"storage-driver":"overlay2"}' | sudo tee /etc/docker/daemon.json - sudo systemctl restart docker - docker version - - name: Login to Docker Hub - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} - if: env.DOCKERHUB_USERNAME != '' - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - name: Pull and save postgres image - shell: bash - run: | - docker pull postgres:latest - docker tag postgres:latest postgres:${{ github.sha }} - docker save postgres:${{ github.sha }} | gzip > postgres-image.tar.gz - - name: Upload postgres image - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: postgres-image - path: postgres-image.tar.gz - retention-days: 10 - build-tailscale-released: - runs-on: ubuntu-24.04-arm - needs: build - if: needs.build.outputs.files-changed == 'true' + checks: ${{ steps.list.outputs.checks }} steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: NixOS/nix-installer-action@6b8548fe06acfb0155a50ab5d561accb215764cc # main - uses: Mic92/hestia/action@ff07bb902a9968ac0c3d0e51d90a606662a375d8 # main - - name: Force overlay2 storage driver + - id: list run: | - sudo mkdir -p /etc/docker - echo '{"storage-driver":"overlay2"}' | sudo tee /etc/docker/daemon.json - sudo systemctl restart docker - docker version - - name: Login to Docker Hub - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CI_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CI_TOKEN }} - if: env.DOCKERHUB_USERNAME != '' - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - name: List Tailscale versions to pre-pull - id: versions - run: | - versions=$(go run ./cmd/hi list-versions --set=must --exclude=head) - echo "versions=${versions}" >> "$GITHUB_OUTPUT" - echo "Pre-pulling: ${versions}" - - name: Pull Tailscale images - run: | - # Releases come from ghcr.io (anonymous, unmetered). The - # "unstable" floating tag on ghcr.io has been stale since 2022, - # so it still needs to come from Docker Hub. xargs -P 0 fans - # out one process per tag and returns non-zero if any pull - # fails. - refs="" - for v in ${{ steps.versions.outputs.versions }}; do - if [ "${v}" = "unstable" ]; then - refs="${refs} tailscale/tailscale:${v}" - else - refs="${refs} ghcr.io/tailscale/tailscale:${v}" - fi - done - echo "${refs}" | tr ' ' '\n' | grep -v '^$' \ - | xargs -P 0 -I{} docker pull "{}" - echo "REFS=${refs}" >> "$GITHUB_ENV" - - name: Save Tailscale images to tarball - run: | - # Single docker save with all refs: one consistent snapshot, no - # parallel-daemon race. - 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' + checks=$(nix eval --json .#checks.x86_64-linux --apply builtins.attrNames \ + | jq -c '[.[] | select(startswith("integration-"))]') + echo "checks=$checks" >> "$GITHUB_OUTPUT" + + prime: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: NixOS/nix-installer-action@6b8548fe06acfb0155a50ab5d561accb215764cc # main + - uses: Mic92/hestia/action@ff07bb902a9968ac0c3d0e51d90a606662a375d8 # main + - run: nix build -L --fallback .#packages.x86_64-linux.integration-deps + + integration: + needs: [list, prime] + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - test: - - TestACLHostsInNetMapTable - - TestACLAllowUser80Dst - - TestACLDenyAllPort80 - - TestACLAllowUserDst - - TestACLAllowStarDst - - TestACLNamedHostsCanReachBySubnet - - TestACLNamedHostsCanReach - - TestACLDevice1CanAccessDevice2 - - TestPolicyUpdateWhileRunningWithCLIInDatabase - - TestACLAutogroupMember - - TestACLAutogroupTagged - - TestACLAutogroupSelf - - TestACLPolicyPropagationOverTime - - TestACLTagPropagation - - TestACLTagPropagationPortSpecific - - TestACLGroupWithUnknownUser - - TestACLGroupAfterUserDeletion - - TestACLGroupDeletionExactReproduction - - TestACLDynamicUnknownUserAddition - - TestACLDynamicUnknownUserRemoval - - TestAPIAuthenticationBypass - - TestAPIAuthenticationBypassCurl - - TestRemoteCLIAuthenticationBypass - - TestCLIWithConfigAuthenticationBypass - - TestAuthKeyLogoutAndReloginSameUser - - TestAuthKeyLogoutAndReloginNewUser - - TestAuthKeyLogoutAndReloginSameUserExpiredKey - - TestAuthKeyDeleteKey - - TestAuthKeyLogoutAndReloginRoutesPreserved - - TestOIDCAuthenticationPingAll - - TestOIDCExpireNodesBasedOnTokenExpiry - - TestOIDC024UserCreation - - TestOIDCAuthenticationWithPKCE - - TestOIDCReloginSameNodeNewUser - - TestOIDCFollowUpUrl - - TestOIDCMultipleOpenedLoginUrls - - TestOIDCReloginSameNodeSameUser - - TestOIDCExpiryAfterRestart - - TestOIDCACLPolicyOnJoin - - TestOIDCReloginSameUserRoutesPreserved - - TestAuthWebFlowAuthenticationPingAll - - TestAuthWebFlowLogoutAndReloginSameUser - - TestAuthWebFlowLogoutAndReloginNewUser - - TestApiKeyCommand - - TestApiKeyCommandValidation - - TestAuthCommandValidation - - TestNodeCommand - - TestNodeExpireCommand - - TestNodeRenameCommand - - TestPreAuthKeyCorrectUserLoggedInCommand - - TestTaggedNodesCLIOutput - - TestNodeExpireFlagsCommand - - TestNodeCommandValidation - - TestNodeTagCommand - - TestNodeRouteCommands - - TestNodeBackfillIPsCommand - - TestOAuthClientCommand - - TestOAuthClientCommandValidation - - TestPolicyCheckCommand - - TestSSHTestsRejectFailingPolicy - - TestPolicyCommand - - TestPolicyBrokenConfigCommand - - TestPreAuthKeyCommand - - TestPreAuthKeyCommandWithoutExpiry - - TestPreAuthKeyCommandReusableEphemeral - - TestPreAuthKeyDeleteCommand - - TestPreAuthKeyCommandValidation - - TestServerInfoCommands - - TestUserCommand - - TestUserCreateCommand - - TestUserCommandValidation - - TestDERPVerifyEndpoint - - TestResolveMagicDNS - - TestResolveMagicDNSExtraRecordsPath - - TestDERPServerScenario - - TestDERPServerWebsocketScenario - - TestPingAllByIP - - TestPingAllByIPPublicDERP - - TestEphemeral - - TestEphemeralInAlternateTimezone - - TestEphemeral2006DeletedTooQuickly - - TestPingAllByHostname - - TestTaildrop - - TestUpdateHostnameFromClient - - TestExpireNode - - TestSetNodeExpiryInFuture - - TestDisableNodeExpiry - - TestNodeOnlineStatus - - TestPingAllByIPManyUpDown - - Test2118DeletingOnlineNodePanics - - TestGrantCapRelay - - TestGrantCapDrive - - TestK8sOperator - - TestEnablingRoutes - - TestHASubnetRouterFailover - - TestSubnetRouteACL - - TestEnablingExitRoutes - - TestExitRoutesWithAutogroupInternetACL - - TestSubnetRouterMultiNetwork - - TestSubnetRouterMultiNetworkExitNode - - TestAutoApproveMultiNetwork/authkey-tag.* - - TestAutoApproveMultiNetwork/authkey-user.* - - TestAutoApproveMultiNetwork/authkey-group.* - - TestAutoApproveMultiNetwork/webauth-tag.* - - TestAutoApproveMultiNetwork/webauth-user.* - - TestAutoApproveMultiNetwork/webauth-group.* - - TestSubnetRouteACLFiltering - - TestGrantViaSubnetSteering - - TestHASubnetRouterPingFailover - - TestHASubnetRouterFailoverBothOffline - - TestHASubnetRouterFailoverBothOfflineCablePull - - TestHASubnetRouterFailoverDockerDisconnect - - TestHeadscale - - TestTailscaleNodesJoiningHeadcale - - TestSSHOneUserToAll - - TestSSHMultipleUsersAllToAll - - TestSSHNoSSHConfigured - - TestSSHIsBlockedInACL - - TestSSHUserOnlyIsolation - - TestSSHAutogroupSelf - - TestSSHOneUserToOneCheckModeCLI - - TestSSHOneUserToOneCheckModeOIDC - - TestSSHCheckModeUnapprovedTimeout - - TestSSHCheckModeCheckPeriodCLI - - TestSSHCheckModeAutoApprove - - TestSSHCheckModeSessionLossReDelegates - - TestSSHCheckModeNegativeCLI - - TestSSHLocalpart - - TestTagsAuthKeyWithTagRequestDifferentTag - - TestTagsAuthKeyWithTagNoAdvertiseFlag - - TestTagsAuthKeyWithTagCannotAddViaCLI - - TestTagsAuthKeyWithTagCannotChangeViaCLI - - TestTagsAuthKeyWithTagAdminOverrideReauthPreserves - - TestTagsAuthKeyWithTagCLICannotModifyAdminTags - - TestTagsAuthKeyWithoutTagCannotRequestTags - - TestTagsAuthKeyWithoutTagRegisterNoTags - - TestTagsAuthKeyWithoutTagCannotAddViaCLI - - TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset - - TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise - - TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag - - TestTagsUserLoginOwnedTagAtRegistration - - TestTagsUserLoginNonExistentTagAtRegistration - - TestTagsUserLoginUnownedTagAtRegistration - - TestTagsUserLoginAddTagViaCLIReauth - - TestTagsUserLoginRemoveTagViaCLIReauth - - TestTagsUserLoginCLINoOpAfterAdminAssignment - - TestTagsUserLoginCLICannotRemoveAdminTags - - TestTagsAuthKeyWithTagRequestNonExistentTag - - TestTagsAuthKeyWithTagRequestUnownedTag - - TestTagsAuthKeyWithoutTagRequestNonExistentTag - - TestTagsAuthKeyWithoutTagRequestUnownedTag - - TestTagsAdminAPICannotSetNonExistentTag - - TestTagsAdminAPICanSetUnownedTag - - TestTagsAdminAPICannotRemoveAllTags - - TestTagsIssue2978ReproTagReplacement - - TestTagsAdminAPICannotSetInvalidFormat - - TestTagsUserLoginReauthWithEmptyTagsRemovesAllTags - - TestTagsAuthKeyWithoutUserInheritsTags - - TestTagsAuthKeyWithoutUserRejectsAdvertisedTags - - TestTagsAuthKeyConvertToUserViaCLIRegister - - TestTailscaleRustAxum - uses: ./.github/workflows/integration-test-template.yml - secrets: inherit - with: - test: ${{ matrix.test }} - postgres_flag: "--postgres=0" - database_name: "sqlite" - postgres: - needs: [build, build-postgres, build-tailscale-released] - if: needs.build.outputs.files-changed == 'true' - strategy: - fail-fast: false - matrix: - test: - - TestACLAllowUserDst - - TestPingAllByIP - - TestEphemeral2006DeletedTooQuickly - - TestPingAllByIPManyUpDown - - TestSubnetRouterMultiNetwork - uses: ./.github/workflows/integration-test-template.yml - secrets: inherit - with: - test: ${{ matrix.test }} - postgres_flag: "--postgres=1" - database_name: "postgres" + check: ${{ fromJson(needs.list.outputs.checks) }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: NixOS/nix-installer-action@6b8548fe06acfb0155a50ab5d561accb215764cc # main + - uses: Mic92/hestia/action@ff07bb902a9968ac0c3d0e51d90a606662a375d8 # main + - name: ${{ matrix.check }} + run: nix build -L --fallback .#checks.x86_64-linux.${{ matrix.check }}