From 3eef1cc07c30596963de6fbac00d1276f92bd6a3 Mon Sep 17 00:00:00 2001 From: mcoulombe Date: Fri, 12 Sep 2025 16:51:45 -0400 Subject: [PATCH 1/2] + added argument to test target connectivity Update action.yml Co-authored-by: Mario Minardi --- README.md | 84 ++++++++++++++++++++++++++++++++++-------------------- action.yml | 43 ++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 57d306f..2fa51b8 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ This GitHub Action connects to your [Tailscale network](https://tailscale.com) by adding a step to your workflow. ```yaml - - name: Tailscale - uses: tailscale/github-action@v3 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - tags: tag:ci +- name: Tailscale + uses: tailscale/github-action@v3 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} + tags: tag:ci ``` Subsequent steps in the Action can then access nodes in your Tailnet. @@ -29,6 +29,28 @@ be automatically removed by the coordination server a short time after they finish their run. The nodes are also [marked Preapproved](https://tailscale.com/kb/1085/auth-keys/) on tailnets which use [Device Approval](https://tailscale.com/kb/1099/device-approval/) +## Eventual consistency + +Propagating information about new peers - such as the node created by this action - across your tailnet +is an eventually consistent process, and brief delays are expected. Until the GitHub workflow node +becomes visible, other peers will not accept connections. It is best to verify connectivity to the +intended nodes before executing steps that rely on them. + +You can do this by adding a list of targets to the action configuration: + +```yaml +- name: Tailscale + uses: tailscale/github-action@v3 + with: + targets: 100.x.y.z,my-machine.my-tailnet.ts.net +``` + +or with the [tailscale ping](https://tailscale.com/kb/1080/cli#ping) command if you do not know the targets at the time of installing Tailscale in the workflow: + +```bash +tailscale ping my-target.my-tailnet.ts.net +``` + ## Tailnet Lock If you are using this Action in a [Tailnet @@ -42,11 +64,11 @@ Lock](https://tailscale.com/kb/1226/tailnet-lock) enabled network, you need to: client to store the Tailnet Key Authority data in. ```yaml - - name: Tailscale - uses: tailscale/github-action@v3 - with: - authkey: tskey-auth-... - statedir: /tmp/tailscale-state/ +- name: Tailscale + uses: tailscale/github-action@v3 + with: + authkey: tskey-auth-... + statedir: /tmp/tailscale-state/ ``` ## Defining Tailscale version @@ -54,25 +76,25 @@ Lock](https://tailscale.com/kb/1226/tailnet-lock) enabled network, you need to: Which Tailscale version to use can be set like this: ```yaml - - name: Tailscale - uses: tailscale/github-action@v3 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - tags: tag:ci - version: 1.52.0 +- name: Tailscale + uses: tailscale/github-action@v3 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} + tags: tag:ci + version: 1.52.0 ``` If you'd like to specify the latest version, simply set the version as `latest` ```yaml - - name: Tailscale - uses: tailscale/github-action@v3 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - tags: tag:ci - version: latest +- name: Tailscale + uses: tailscale/github-action@v3 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} + tags: tag:ci + version: latest ``` You can find the latest Tailscale stable version number at @@ -86,10 +108,10 @@ Caching can reduce download times and download failures on runners with slower n You can opt in to caching Tailscale binaries by passing `'true'` to the `use-cache` input: ```yaml - - name: Tailscale - uses: tailscale/github-action@v3 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - use-cache: 'true' +- name: Tailscale + uses: tailscale/github-action@v3 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} + use-cache: 'true' ``` diff --git a/action.yml b/action.yml index 7076d5a..f678897 100644 --- a/action.yml +++ b/action.yml @@ -56,6 +56,10 @@ inputs: description: 'Whether to cache the Tailscale binaries (Linux/macOS) or installer (Windows)' required: false default: 'false' + targets: + description: 'Comma separated list of targets (Tailscale IP addresses or machine names if MagicDNS is enabled on the tailnet) to `tailscale ping` for connectivity verification after `tailscale up` completes' + required: false + default: '' runs: using: 'composite' steps: @@ -150,7 +154,7 @@ runs: URL="https://pkgs.tailscale.com/unstable/tailscale_${RESOLVED_VERSION}_${TS_ARCH}.tgz" fi echo "Downloading $URL" - curl -H user-agent:tailscale-github-action -L "$URL" -o tailscale.tgz --max-time 300 --fail + curl -H user-agent:tailscale-github-action -L "$URL" -o tailscale.tgz --max-time 300 --retry 3 --retry-all-errors --fail echo "Expected sha256: $SHA256SUM" echo "Actual sha256: $(sha256sum tailscale.tgz)" echo "$SHA256SUM tailscale.tgz" | sha256sum -c @@ -209,7 +213,7 @@ runs: URL="https://pkgs.tailscale.com/unstable/tailscale-setup-${RESOLVED_VERSION}-${TS_ARCH}.msi" fi echo "Downloading $URL" - curl -H user-agent:tailscale-github-action -L "$URL" -o tailscale.msi --max-time 300 --fail + curl -H user-agent:tailscale-github-action -L "$URL" -o tailscale.msi --max-time 300 --retry 3 --retry-all-errors --fail echo "Expected sha256: $SHA256SUM" echo "Actual sha256: $(sha256sum tailscale.msi)" echo "$SHA256SUM tailscale.msi" | sha256sum -c @@ -353,3 +357,38 @@ runs: echo "Tailscale up failed. Retrying in $((i * 5)) seconds..." sleep $((i * 5)) done + - name: Verify Target Connectivity + if: ${{ inputs.targets != '' }} + shell: bash + env: + TARGETS: ${{ inputs.targets }} + run: | + IFS=',' read -ra TARGET_ARRAY <<< "$TARGETS" + + if [ "${{ runner.os }}" != "Windows" ]; then + MAYBE_SUDO="sudo -E" + fi + + failed_targets=() + for target in "${TARGET_ARRAY[@]}"; do + target=$(echo "$target" | xargs) # trim whitespace + if [ -n "$target" ]; then + output=$(${MAYBE_SUDO} tailscale ping --c=36 $target 2>&1) + exit_code=$? + + if [ $exit_code -eq 0 ]; then + echo "Successfully reached $target" + elif echo "$output" | grep -q "direct connection not established"; then + echo "::warning title=Target Connectivity Warning::Failed to establish direct connection to $target, but connection may still work via DERP" + else + # Regular failure case + echo "Failed to reach $target" + failed_targets+=("$target") + fi + fi + done + + if [ ${#failed_targets[@]} -gt 0 ]; then + echo "::error title=Target Connectivity Failed::Failed to reach the following targets: ${failed_targets[*]}" + exit 1 + fi From 2208a5a58b38b9571b0c01b362502f331e307268 Mon Sep 17 00:00:00 2001 From: Max Coulombe Date: Thu, 25 Sep 2025 11:40:17 -0400 Subject: [PATCH 2/2] Update action.yml Co-authored-by: Mario Minardi --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index f678897..ba92d07 100644 --- a/action.yml +++ b/action.yml @@ -379,7 +379,7 @@ runs: if [ $exit_code -eq 0 ]; then echo "Successfully reached $target" elif echo "$output" | grep -q "direct connection not established"; then - echo "::warning title=Target Connectivity Warning::Failed to establish direct connection to $target, but connection may still work via DERP" + echo "::warning title=Target Connectivity Warning::Failed to establish direct connection to $target but was able to connect via DERP" else # Regular failure case echo "Failed to reach $target"