Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/actions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ workflows:
- 'asana/push-signed-commits@d615ca88d8e1a946734c24970d1e7a6c56f34897'
'.github/workflows/spark-theatre-gate.yml':
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
'.github/workflows/tailscale-connect-reusable.yml':
- 'tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888'
'.github/workflows/tag-ruleset-canon.yml':
- 'actions/checkout@v7.0.1'
- 'actions/create-github-app-token@v3.2.0'
Expand Down
44 changes: 41 additions & 3 deletions .github/workflows/tailscale-connect-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
# Tailscale OAuth client. The two secrets below must exist on the repo, org or
# environment that calls this workflow:
# TS_OAUTH_CLIENT_ID, TS_OAUTH_SECRET (Tailscale admin -> OAuth clients, scope auth_keys)
# Give the client an ACL tag (default `tag:ci`) so runners are an ephemeral,
# auditable set rather than named machines.
#
# Callers opt in per job; nothing here changes CI unless a workflow calls it.
# Pinned to v4.1.3 (780049a30b6ff5c378a9e7b389d15ece7a204888). The action also publishes a moving `v4`
# tag; as of 2026-09-20 that tag points at v4.1.2, so `@v4` would resolve to a
# different commit than the one audited here -- which is the reason estate
# policy pins 40-hex SHAs and records them in actions.lock.
#
# Callers opt in per job. When the secrets are absent the job skips with a
# warning instead of failing, so adding this to a repo is safe before the
# credentials land; pass `require: true` to make that case red on purpose.
name: Tailscale connect (reusable)

on:
Expand All @@ -31,6 +40,11 @@
required: false
type: string
default: ''
require:
description: 'Fail instead of skipping when TS_OAUTH_* are unavailable'
required: false
type: boolean
default: false

permissions:
contents: read
Expand All @@ -40,10 +54,34 @@
name: Join tailnet
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
connected: ${{ steps.gate.outputs.ready }}
steps:
# Pinned per estate policy (SHA pins everywhere); refresh with actions-lock.
- name: Check tailnet credentials
id: gate
env:
TS_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
TS_SECRET: ${{ secrets.TS_OAUTH_SECRET }}
REQUIRE: ${{ inputs.require }}
run: |
if [ -n "$TS_ID" ] && [ -n "$TS_SECRET" ]; then
printf 'ready=true\n' >> "$GITHUB_OUTPUT"
exit 0
fi
printf 'ready=false\n' >> "$GITHUB_OUTPUT"
if [ "$REQUIRE" = "true" ]; then
echo "::error::TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET are not available to this job." \
"Set them on the repository, environment or org (Tailscale admin console ->" \
"OAuth clients, scope auth_keys), or drop require: true."
exit 1
fi
echo "::warning::No Tailscale credentials available -- skipping the tailnet connect step." \
"Any job that depends on tailnet reachability will fail to reach private endpoints."

# Pinned per estate policy (SHA pins everywhere); refresh with `gh actions-lock`.
- name: Connect to Tailscale
uses: tailscale/github-action@v4
if: steps.gate.outputs.ready == 'true'
uses: tailscale/github-action@780049a30b6ff5c378a9e7b389d15ece7a204888 # v4.1.3
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
Expand Down
Loading