From 8a3cfd2e9e447ed4f1285c11321829ab9e1029ea Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Wed, 1 Oct 2025 10:52:51 -0500 Subject: [PATCH] fix bugs in ping logic, ping hosts in parallel, and tweak README and LICENSE The ping logic had some bugs in the wait logic which are now fixed. Pinging hosts in parallel will speed up CI. LICENSE and README got small updates in preparation for moving this code to tailscale/github-action. Updates tailscale/corp#32862 Signed-off-by: Percy Wegmann --- LICENSE | 2 +- README.md | 14 ++++++++------ dist/index.js | 22 +++++++++++++++------- src/main.ts | 22 +++++++++++++++------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index d3e245c..7405524 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ BSD 3-Clause License +Copyright (c) 2020 Tailscale Inc & Contributors Copyright (c) 2025 Lee Briggs -Copyright (c) 2025 Tailscale Inc & Contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index d060596..cbbfd28 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ by adding a step to your workflow. ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} @@ -40,7 +40,7 @@ You can do this by adding a list of hosts to ping to the action configuration: ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: ping: 100.x.y.z,my-machine.my-tailnet.ts.net ``` @@ -51,6 +51,8 @@ or with the [tailscale ping](https://tailscale.com/kb/1080/cli#ping) command if tailscale ping my-target.my-tailnet.ts.net ``` +The `ping` option will wait up to to 3 minutes for a connection (direct or relayed). + > ⚠️ On macOS runners, one can only ping IP addresses, not hostnames. ## Tailnet Lock @@ -67,7 +69,7 @@ Lock](https://tailscale.com/kb/1226/tailnet-lock) enabled network, you need to: ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: authkey: tskey-auth-... statedir: /tmp/tailscale-state/ @@ -79,7 +81,7 @@ Which Tailscale version to use can be set like this: ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} @@ -91,7 +93,7 @@ If you'd like to specify the latest version, simply set the version as `latest` ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} @@ -115,7 +117,7 @@ Although caching is generally recommended, you can disable it by passing `'false ```yaml - name: Tailscale - uses: tailscale/github-action@v3 + uses: tailscale/github-action@v4 with: oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} diff --git a/dist/index.js b/dist/index.js index e2c0dcc..0d1483e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -41270,17 +41270,23 @@ async function pingHostsIfNecessary(config) { if (config.pingHosts.length == 0) { return; } - core.info(`Will ping hosts ${config.pingHosts.join(",")} up to 3 minutes each in order to check connectivity`); - for (const host of config.pingHosts) { - await pingHost(host); + core.info(`Will ping hosts ${config.pingHosts.join(",")} up to 3 minutes each (in parallel) in order to check connectivity`); + let pings = config.pingHosts.map((host) => pingHost(host)); + for (const ping of pings) { + await ping; } } async function pingHost(host) { core.info(`Pinging host ${host}`); - for (let i = 0; i <= 36; i++) { - if (i < 0) { - // wait 5 seconds before pinging - (0, promises_1.setTimeout)(5000); + let start = new Date().getTime(); + var i = 0; + // Try for up to 180 seconds (3 minutes). + while ((new Date().getTime() - start) / 1000 < 180) { + if (i > 0) { + // Exponential backoff on wait time, with maximum 5 second wait. + let waitTime = Math.min(Math.pow(1.3, i), 5000); + core.debug(`Waiting ${waitTime} milliseconds before pinging`); + await (0, promises_1.setTimeout)(waitTime); } let result = await exec.getExecOutput(cmdTailscale, ["ping", "-c", "1", host], { ignoreReturnCode: true }); if (result.exitCode === 0) { @@ -41288,9 +41294,11 @@ async function pingHost(host) { return; } else if (result.stderr.includes("direct connection not established")) { + // Relayed connectivity is good enough, we don't want to tie up a CI job waiting for a direct connection. core.info(`✅ Ping host ${host} reachable via DERP!`); return; } + i++; } core.setFailed(`❌ Ping host ${host} did not respond`); process.exit(1); diff --git a/src/main.ts b/src/main.ts index ca1dc20..ebce791 100644 --- a/src/main.ts +++ b/src/main.ts @@ -199,19 +199,25 @@ async function pingHostsIfNecessary(config: TailscaleConfig): Promise { core.info( `Will ping hosts ${config.pingHosts.join( "," - )} up to 3 minutes each in order to check connectivity` + )} up to 3 minutes each (in parallel) in order to check connectivity` ); - for (const host of config.pingHosts) { - await pingHost(host); + let pings = config.pingHosts.map((host) => pingHost(host)); + for (const ping of pings) { + await ping; } } async function pingHost(host: string): Promise { core.info(`Pinging host ${host}`); - for (let i = 0; i <= 36; i++) { - if (i < 0) { - // wait 5 seconds before pinging - wait(5000); + let start = new Date().getTime(); + var i = 0; + // Try for up to 180 seconds (3 minutes). + while ((new Date().getTime() - start) / 1000 < 180) { + if (i > 0) { + // Exponential backoff on wait time, with maximum 5 second wait. + let waitTime = Math.min(Math.pow(1.3, i), 5000); + core.debug(`Waiting ${waitTime} milliseconds before pinging`); + await wait(waitTime); } let result = await exec.getExecOutput( cmdTailscale, @@ -222,9 +228,11 @@ async function pingHost(host: string): Promise { core.info(`✅ Ping host ${host} reachable via direct connection!`); return; } else if (result.stderr.includes("direct connection not established")) { + // Relayed connectivity is good enough, we don't want to tie up a CI job waiting for a direct connection. core.info(`✅ Ping host ${host} reachable via DERP!`); return; } + i++; } core.setFailed(`❌ Ping host ${host} did not respond`); process.exit(1);