@@ -41129,26 +41129,17 @@ const cmdTailscale = "tailscale";
4112941129const cmdTailscaleFullPath = "/usr/local/bin/tailscale";
4113041130const cmdTailscaled = "tailscaled";
4113141131const cmdTailscaledFullPath = "/usr/local/bin/tailscaled";
41132- const platformWin32 = "win32";
41133- const platformDarwin = "darwin";
4113441132const runnerLinux = "Linux";
4113541133const runnerWindows = "Windows";
4113641134const runnerMacOS = "macOS";
4113741135const versionLatest = "latest";
4113841136const versionUnstable = "unstable";
4113941137// Cross-platform Tailscale local API status check
4114041138async function getTailscaleStatus() {
41141- const { exitCode, stdout, stderr } = await exec.getExecOutput(cmdTailscale, ["status", "--json"], {
41142- silent: true,
41143- ignoreReturnCode: true,
41144- });
41145- if (exitCode !== 0) {
41146- process.stderr.write(stderr);
41147- throw new Error(`tailscale status failed with exit code ${exitCode}`);
41148- }
41149- if (core.isDebug()) {
41150- process.stdout.write(stdout);
41151- }
41139+ const { stdout } = await execSilent("get tailscale status", cmdTailscale, [
41140+ "status",
41141+ "--json",
41142+ ]);
4115241143 return JSON.parse(stdout);
4115341144}
4115441145async function run() {
@@ -41236,15 +41227,23 @@ async function pingHost(host) {
4123641227 core.debug(`Waiting ${waitTime} milliseconds before pinging`);
4123741228 await (0, promises_1.setTimeout)(waitTime);
4123841229 }
41239- let result = await exec.getExecOutput(cmdTailscale, ["ping", "-c", "1", host], { ignoreReturnCode: true });
41240- if (result.exitCode === 0) {
41230+ try {
41231+ let result = await execSilent("ping host", cmdTailscale, [
41232+ "ping",
41233+ "-c",
41234+ "1",
41235+ host,
41236+ ]);
4124141237 core.info(`✅ Ping host ${host} reachable via direct connection!`);
4124241238 return;
4124341239 }
41244- else if (result.stderr.includes("direct connection not established")) {
41245- // Relayed connectivity is good enough, we don't want to tie up a CI job waiting for a direct connection.
41246- core.info(`✅ Ping host ${host} reachable via DERP!`);
41247- return;
41240+ catch (err) {
41241+ if (err instanceof execError &&
41242+ err.stderr.includes("direct connection not established")) {
41243+ // Relayed connectivity is good enough, we don't want to tie up a CI job waiting for a direct connection.
41244+ core.info(`✅ Ping host ${host} reachable via DERP!`);
41245+ return;
41246+ }
4124841247 }
4124941248 i++;
4125041249 }
@@ -41284,7 +41283,8 @@ async function resolveVersion(version, runnerOS) {
4128441283 }
4128541284 if (version === versionLatest || version === versionUnstable) {
4128641285 let path = version === versionUnstable ? versionUnstable : "stable";
41287- const { stdout } = await exec.getExecOutput("curl", [
41286+ let pkg = `https://pkgs.tailscale.com/${path}/?mode=json`;
41287+ const { stdout } = await execSilent(`curl ${pkg}`, "curl", [
4128841288 "-H",
4128941289 "user-agent:action-setup-tailscale",
4129041290 "-s",
@@ -41389,7 +41389,7 @@ async function installTailscaleLinux(config, toolPath) {
4138941389 // Get SHA256 if not provided
4139041390 if (!config.sha256Sum) {
4139141391 const shaUrl = `${baseUrl}/tailscale_${config.resolvedVersion}_${config.arch}.tgz.sha256`;
41392- const { stdout } = await exec.getExecOutput( "curl", [
41392+ const { stdout } = await execSilent(`curl ${shaUrl}`, "curl", [
4139341393 "-H",
4139441394 "user-agent:action-setup-tailscale",
4139541395 "-L",
@@ -41418,15 +41418,23 @@ async function installTailscaleLinux(config, toolPath) {
4141841418 fs.copyFileSync(path.join(extractedDir, cmdTailscale), path.join(toolPath, cmdTailscale));
4141941419 fs.copyFileSync(path.join(extractedDir, cmdTailscaled), path.join(toolPath, cmdTailscaled));
4142041420 // Install binaries to /usr/local/bin
41421- await exec.exec( "sudo", [
41421+ await execSilent("copy tailscale binaries to /usr/local/bin", "sudo", [
4142241422 "cp",
4142341423 path.join(toolPath, cmdTailscale),
4142441424 path.join(toolPath, cmdTailscaled),
4142541425 "/usr/local/bin",
4142641426 ]);
4142741427 // Make sure they're executable
41428- await exec.exec("sudo", ["chmod", "+x", cmdTailscaleFullPath]);
41429- await exec.exec("sudo", ["chmod", "+x", cmdTailscaledFullPath]);
41428+ await execSilent("chmod tailscale binary", "sudo", [
41429+ "chmod",
41430+ "+x",
41431+ cmdTailscaleFullPath,
41432+ ]);
41433+ await execSilent("chmod tailscaled binary", "sudo", [
41434+ "chmod",
41435+ "+x",
41436+ cmdTailscaledFullPath,
41437+ ]);
4143041438}
4143141439async function installTailscaleWindows(config, toolPath, fromCache = false) {
4143241440 // Create tool directory
@@ -41450,7 +41458,7 @@ async function installTailscaleWindows(config, toolPath, fromCache = false) {
4145041458 // Get SHA256 if not provided
4145141459 if (!config.sha256Sum) {
4145241460 const shaUrl = `${baseUrl}/tailscale-setup-${config.resolvedVersion}-${config.arch}.msi.sha256`;
41453- const { stdout } = await exec.getExecOutput( "curl", [
41461+ const { stdout } = await execSilent(`curl ${shaUrl}`, "curl", [
4145441462 "-H",
4145541463 "user-agent:action-setup-tailscale",
4145641464 "-L",
@@ -41478,7 +41486,7 @@ async function installTailscaleWindows(config, toolPath, fromCache = false) {
4147841486 }
4147941487 }
4148041488 // Install MSI (same for both fresh and cached)
41481- await exec.exec( "msiexec.exe", [
41489+ await execSilent("install msi", "msiexec.exe", [
4148241490 "/quiet",
4148341491 `/l*v`,
4148441492 path.join(process.env.RUNNER_TEMP || "", "tailscale.log"),
@@ -41491,16 +41499,16 @@ async function installTailscaleWindows(config, toolPath, fromCache = false) {
4149141499async function installTailscaleMacOS(config, toolPath) {
4149241500 core.info("Building tailscale from src on macOS...");
4149341501 // Clone the repo
41494- await exec.exec( "git clone https://github.com/tailscale/tailscale.git tailscale");
41502+ await execSilent("glone tailscale repo", "git clone https://github.com/tailscale/tailscale.git tailscale");
4149541503 // Checkout the resolved version
41496- await exec.exec( `git checkout v${config.resolvedVersion}`, [], {
41504+ await execSilent("checkout resolved version", `git checkout v${config.resolvedVersion}`, [], {
4149741505 cwd: cmdTailscale,
4149841506 });
4149941507 // Create tool directory and copy binaries there for caching
4150041508 fs.mkdirSync(toolPath, { recursive: true });
4150141509 // Build tailscale and tailscaled into tool directory
4150241510 for (const binary of [cmdTailscale, cmdTailscaled]) {
41503- await exec.exec( `./build_dist.sh -o ${path.join(toolPath, binary)} ./cmd/${binary}`, [], {
41511+ await execSilent(`build ${binary}`, `./build_dist.sh -o ${path.join(toolPath, binary)} ./cmd/${binary}`, [], {
4150441512 cwd: cmdTailscale,
4150541513 env: {
4150641514 ...process.env,
@@ -41509,15 +41517,23 @@ async function installTailscaleMacOS(config, toolPath) {
4150941517 });
4151041518 }
4151141519 // Install binaries to /usr/local/bin
41512- await exec.exec( "sudo", [
41520+ await execSilent("copy binaries to /usr/local/bin", "sudo", [
4151341521 "cp",
4151441522 path.join(toolPath, cmdTailscale),
4151541523 path.join(toolPath, cmdTailscaled),
4151641524 "/usr/local/bin",
4151741525 ]);
4151841526 // Make sure they're executable
41519- await exec.exec("sudo", ["chmod", "+x", cmdTailscaleFullPath]);
41520- await exec.exec("sudo", ["chmod", "+x", cmdTailscaledFullPath]);
41527+ await execSilent("chmod tailscale", "sudo", [
41528+ "chmod",
41529+ "+x",
41530+ cmdTailscaleFullPath,
41531+ ]);
41532+ await execSilent("chmod tailscaled", "sudo", [
41533+ "chmod",
41534+ "+x",
41535+ cmdTailscaledFullPath,
41536+ ]);
4152141537 core.info("✅ Tailscale installed successfully on macOS from source");
4152241538}
4152341539async function startTailscaleDaemon(config) {
@@ -41588,7 +41604,7 @@ async function connectToTailscale(config, runnerOS) {
4158841604 hostname = `github-${process.env.COMPUTERNAME}`;
4158941605 }
4159041606 else {
41591- const { stdout } = await exec.getExecOutput( "hostname");
41607+ const { stdout } = await execSilent("hostname", "hostname");
4159241608 hostname = `github-${stdout.trim()}`;
4159341609 }
4159441610 }
@@ -41631,9 +41647,8 @@ async function connectToTailscale(config, runnerOS) {
4163141647 execArgs = ["sudo", "-E", cmdTailscale, ...upArgs];
4163241648 }
4163341649 const timeoutMs = parseTimeout(config.timeout);
41634- core.info(`Running: ${execArgs.join(" ")} (timeout: ${timeoutMs}ms)`);
4163541650 await Promise.race([
41636- exec.exec( execArgs[0], execArgs.slice(1)),
41651+ execSilent("tailscale up", execArgs[0], execArgs.slice(1)),
4163741652 new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), timeoutMs)),
4163841653 ]);
4163941654 // Success
@@ -41690,10 +41705,26 @@ async function installCachedBinaries(toolPath, runnerOS) {
4169041705 const tailscaleBin = path.join(toolPath, cmdTailscale);
4169141706 const tailscaledBin = path.join(toolPath, cmdTailscaled);
4169241707 if (fs.existsSync(tailscaleBin) && fs.existsSync(tailscaledBin)) {
41693- await exec.exec("sudo", ["cp", tailscaleBin, cmdTailscaleFullPath]);
41694- await exec.exec("sudo", ["cp", tailscaledBin, cmdTailscaledFullPath]);
41695- await exec.exec("sudo", ["chmod", "+x", cmdTailscaleFullPath]);
41696- await exec.exec("sudo", ["chmod", "+x", cmdTailscaledFullPath]);
41708+ await execSilent("copy tailscale from cache", "sudo", [
41709+ "cp",
41710+ tailscaleBin,
41711+ cmdTailscaleFullPath,
41712+ ]);
41713+ await execSilent("copy tailscaled from cache", "sudo", [
41714+ "cp",
41715+ tailscaledBin,
41716+ cmdTailscaledFullPath,
41717+ ]);
41718+ await execSilent("chmod tailscale", "sudo", [
41719+ "chmod",
41720+ "+x",
41721+ cmdTailscaleFullPath,
41722+ ]);
41723+ await execSilent("chmod tailscaled", "sudo", [
41724+ "chmod",
41725+ "+x",
41726+ cmdTailscaledFullPath,
41727+ ]);
4169741728 }
4169841729 else {
4169941730 throw new Error(`Cached binaries not found in ${toolPath}`);
@@ -41707,12 +41738,12 @@ async function configureDNSOnMacOS(status) {
4170741738 }
4170841739 core.info(`Setting system DNS server to 100.100.100.100 and searchdomains to ${status.CurrentTailnet.MagicDNSSuffix}`);
4170941740 try {
41710- await exec.exec( "networksetup", [
41741+ await execSilent("set dns servers", "networksetup", [
4171141742 "-setdnsservers",
4171241743 "Ethernet",
4171341744 "100.100.100.100",
4171441745 ]);
41715- await exec.exec( "networksetup", [
41746+ await execSilent("set search domains", "networksetup", [
4171641747 "-setsearchdomains",
4171741748 "Ethernet",
4171841749 status.CurrentTailnet.MagicDNSSuffix,
@@ -41723,6 +41754,45 @@ async function configureDNSOnMacOS(status) {
4172341754 }
4172441755}
4172541756run();
41757+ /**
41758+ * Executes the given command, logging the given label as info, but suppressing
41759+ * all other output including the command line itself (unless debug logging is enabled,
41760+ * see https://docs.github.com/en/actions/how-tos/monitor-workflows/enable-debug-logging).
41761+ *
41762+ * If the command fails, stderr is written to the console.
41763+ *
41764+ * @param label a label to use for info logging what's happening
41765+ * @param cmd the command to run
41766+ * @param args arguments to the command
41767+ * @returns stdout (if command was successful)
41768+ * @throws execError if exec returned a non-zero status code
41769+ */
41770+ async function execSilent(label, cmd, args, opts) {
41771+ core.info(`▶️ ${label}`);
41772+ const out = await exec.getExecOutput(cmd, args, {
41773+ ...opts,
41774+ silent: !core.isDebug(),
41775+ ignoreReturnCode: true,
41776+ });
41777+ if (out.exitCode !== 0) {
41778+ if (!core.isDebug) {
41779+ // When debug logging is off, stderr won't have been written to console, write it now.
41780+ process.stderr.write(out.stderr);
41781+ }
41782+ throw new execError(`${cmd} failed with exit code ${out.exitCode}`, out.exitCode, out.stderr);
41783+ }
41784+ return out;
41785+ }
41786+ class execError {
41787+ constructor(msg, exitCode, stderr) {
41788+ this.msg = msg;
41789+ this.exitCode = exitCode;
41790+ this.stderr = stderr;
41791+ }
41792+ toString() {
41793+ return this.msg;
41794+ }
41795+ }
4172641796
4172741797
4172841798/***/ }),
0 commit comments