diff --git a/README.md b/README.md index ae2b0c21..bcb01ff5 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ You can then [manually run a workflow](https://docs.github.com/en/actions/managi ## Without sudo -By default we run the commands using sudo. If you get `sudo: not found` you can use the parameter below to execute the commands directly. +By default we run installation commands using sudo on Linux. If you get `sudo: not found` you can use the parameter below to execute the commands directly. ```yaml name: CI @@ -165,6 +165,22 @@ jobs: tmate-server-ed25519-fingerprint: SHA256:jfttvoypkHiQYUqUCwKeqd9d1fJj/ZiQlFOHVl6E9sI ``` +## Skip installing tmate + +By default, tmate and its dependencies are installed in a platform-dependent manner. When using self-hosted agents, this can become unnecessary or can even break. You can skip installing tmate and its dependencies using `install-dependencies`: + +```yaml +name: CI +on: [push] +jobs: + build: + runs-on: [self-hosted, linux] + steps: + - uses: mxschmitt/action-tmate@v3 + with: + install-dependencies: false +``` + ## Continue a workflow If you want to continue a workflow and you are inside a tmate session, just create a empty file with the name `continue` either in the root directory or in the project directory by running `touch continue` or `sudo touch /continue`. diff --git a/action.yml b/action.yml index 3e84a28b..e641e7e0 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,10 @@ inputs: description: 'If apt should be executed with sudo or without' required: false default: 'true' + install-dependencies: + description: 'Whether or not to install dependencies for tmate on linux (openssh-client, xz-utils)' + required: false + default: 'true' limit-access-to-actor: description: 'If only the public SSH keys of the user triggering the workflow should be authorized' required: false diff --git a/lib/index.js b/lib/index.js index 5fada82b..e670611d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10334,44 +10334,46 @@ const TMATE_ARCH_MAP = { const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); async function run() { - const optionalSudoPrefix = core.getInput('sudo') === "true" ? "sudo " : ""; try { - core.debug("Installing dependencies") let tmateExecutable = "tmate" - if (process.platform === "darwin") { - await execShellCommand('brew install tmate'); - } else if (process.platform === "win32") { - await execShellCommand('pacman -Sy --noconfirm tmate'); - tmateExecutable = 'CHERE_INVOKING=1 tmate' - } else { - const distro = await getLinuxDistro(); - core.debug("linux distro: [" + distro + "]"); - if (distro === "alpine") { - // for set -e workaround, we need to install bash because alpine doesn't have it - await execShellCommand(optionalSudoPrefix + 'apk add openssh-client xz bash'); + if (core.getInput("install-dependencies") !== "false") { + core.debug("Installing dependencies") + if (process.platform === "darwin") { + await execShellCommand('brew install tmate'); + } else if (process.platform === "win32") { + await execShellCommand('pacman -Sy --noconfirm tmate'); } else { - await execShellCommand(optionalSudoPrefix + 'apt-get update'); - await execShellCommand(optionalSudoPrefix + 'apt-get install -y openssh-client xz-utils'); - } + const optionalSudoPrefix = core.getInput("sudo") === "true" ? "sudo " : ""; + const distro = await getLinuxDistro(); + core.debug("linux distro: [" + distro + "]"); + if (distro === "alpine") { + // for set -e workaround, we need to install bash because alpine doesn't have it + await execShellCommand(optionalSudoPrefix + 'apk add openssh-client xz bash'); + } else { + await execShellCommand(optionalSudoPrefix + 'apt-get update'); + await execShellCommand(optionalSudoPrefix + 'apt-get install -y openssh-client xz-utils'); + } - const tmateArch = TMATE_ARCH_MAP[external_os_default().arch()]; - if (!tmateArch) { - throw new Error(`Unsupported architecture: ${external_os_default().arch()}`) - } - const tmateReleaseTar = await tool_cache.downloadTool(`https://github.com/tmate-io/tmate/releases/download/${TMATE_LINUX_VERSION}/tmate-${TMATE_LINUX_VERSION}-static-linux-${tmateArch}.tar.xz`); - const tmateDir = external_path_default().join(external_os_default().tmpdir(), "tmate") - tmateExecutable = external_path_default().join(tmateDir, "tmate") + const tmateArch = TMATE_ARCH_MAP[external_os_default().arch()]; + if (!tmateArch) { + throw new Error(`Unsupported architecture: ${external_os_default().arch()}`) + } + const tmateReleaseTar = await tool_cache.downloadTool(`https://github.com/tmate-io/tmate/releases/download/${TMATE_LINUX_VERSION}/tmate-${TMATE_LINUX_VERSION}-static-linux-${tmateArch}.tar.xz`); + const tmateDir = external_path_default().join(external_os_default().tmpdir(), "tmate") + tmateExecutable = external_path_default().join(tmateDir, "tmate") - if (external_fs_default().existsSync(tmateExecutable)) - external_fs_default().unlinkSync(tmateExecutable) - external_fs_default().mkdirSync(tmateDir, { recursive: true }) - await execShellCommand(`tar x -C ${tmateDir} -f ${tmateReleaseTar} --strip-components=1`) - external_fs_default().unlinkSync(tmateReleaseTar) + if (external_fs_default().existsSync(tmateExecutable)) + external_fs_default().unlinkSync(tmateExecutable) + external_fs_default().mkdirSync(tmateDir, { recursive: true }) + await execShellCommand(`tar x -C ${tmateDir} -f ${tmateReleaseTar} --strip-components=1`) + external_fs_default().unlinkSync(tmateReleaseTar) + } + core.debug("Installed dependencies successfully"); } - core.debug("Installed dependencies successfully"); - - if (process.platform !== "win32") { + if (process.platform === "win32") { + tmateExecutable = 'CHERE_INVOKING=1 tmate' + } else { core.debug("Generating SSH keys") external_fs_default().mkdirSync(external_path_default().join(external_os_default().homedir(), ".ssh"), { recursive: true }) try { diff --git a/src/index.js b/src/index.js index a74c45ca..4855b32c 100644 --- a/src/index.js +++ b/src/index.js @@ -25,44 +25,46 @@ const TMATE_ARCH_MAP = { const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); export async function run() { - const optionalSudoPrefix = core.getInput('sudo') === "true" ? "sudo " : ""; try { - core.debug("Installing dependencies") let tmateExecutable = "tmate" - if (process.platform === "darwin") { - await execShellCommand('brew install tmate'); - } else if (process.platform === "win32") { - await execShellCommand('pacman -Sy --noconfirm tmate'); - tmateExecutable = 'CHERE_INVOKING=1 tmate' - } else { - const distro = await getLinuxDistro(); - core.debug("linux distro: [" + distro + "]"); - if (distro === "alpine") { - // for set -e workaround, we need to install bash because alpine doesn't have it - await execShellCommand(optionalSudoPrefix + 'apk add openssh-client xz bash'); + if (core.getInput("install-dependencies") !== "false") { + core.debug("Installing dependencies") + if (process.platform === "darwin") { + await execShellCommand('brew install tmate'); + } else if (process.platform === "win32") { + await execShellCommand('pacman -Sy --noconfirm tmate'); } else { - await execShellCommand(optionalSudoPrefix + 'apt-get update'); - await execShellCommand(optionalSudoPrefix + 'apt-get install -y openssh-client xz-utils'); - } - - const tmateArch = TMATE_ARCH_MAP[os.arch()]; - if (!tmateArch) { - throw new Error(`Unsupported architecture: ${os.arch()}`) + const optionalSudoPrefix = core.getInput("sudo") === "true" ? "sudo " : ""; + const distro = await getLinuxDistro(); + core.debug("linux distro: [" + distro + "]"); + if (distro === "alpine") { + // for set -e workaround, we need to install bash because alpine doesn't have it + await execShellCommand(optionalSudoPrefix + 'apk add openssh-client xz bash'); + } else { + await execShellCommand(optionalSudoPrefix + 'apt-get update'); + await execShellCommand(optionalSudoPrefix + 'apt-get install -y openssh-client xz-utils'); + } + + const tmateArch = TMATE_ARCH_MAP[os.arch()]; + if (!tmateArch) { + throw new Error(`Unsupported architecture: ${os.arch()}`) + } + const tmateReleaseTar = await tc.downloadTool(`https://github.com/tmate-io/tmate/releases/download/${TMATE_LINUX_VERSION}/tmate-${TMATE_LINUX_VERSION}-static-linux-${tmateArch}.tar.xz`); + const tmateDir = path.join(os.tmpdir(), "tmate") + tmateExecutable = path.join(tmateDir, "tmate") + + if (fs.existsSync(tmateExecutable)) + fs.unlinkSync(tmateExecutable) + fs.mkdirSync(tmateDir, { recursive: true }) + await execShellCommand(`tar x -C ${tmateDir} -f ${tmateReleaseTar} --strip-components=1`) + fs.unlinkSync(tmateReleaseTar) } - const tmateReleaseTar = await tc.downloadTool(`https://github.com/tmate-io/tmate/releases/download/${TMATE_LINUX_VERSION}/tmate-${TMATE_LINUX_VERSION}-static-linux-${tmateArch}.tar.xz`); - const tmateDir = path.join(os.tmpdir(), "tmate") - tmateExecutable = path.join(tmateDir, "tmate") - - if (fs.existsSync(tmateExecutable)) - fs.unlinkSync(tmateExecutable) - fs.mkdirSync(tmateDir, { recursive: true }) - await execShellCommand(`tar x -C ${tmateDir} -f ${tmateReleaseTar} --strip-components=1`) - fs.unlinkSync(tmateReleaseTar) + core.debug("Installed dependencies successfully"); } - core.debug("Installed dependencies successfully"); - - if (process.platform !== "win32") { + if (process.platform === "win32") { + tmateExecutable = 'CHERE_INVOKING=1 tmate' + } else { core.debug("Generating SSH keys") fs.mkdirSync(path.join(os.homedir(), ".ssh"), { recursive: true }) try { diff --git a/src/index.test.js b/src/index.test.js index 53659bc9..658c0fcc 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -28,11 +28,24 @@ describe('Tmate GitHub integration', () => { Object.defineProperty(process, "platform", { value: "win32" }) - core.getInput.mockReturnValueOnce("true").mockReturnValue("false") + core.getInput.mockReturnValueOnce("true").mockReturnValueOnce("false") const customConnectionString = "foobar" execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)) await run() - expect(execShellCommand).toHaveBeenNthCalledWith(1, "pacman -Sy --noconfirm tmate") + expect(execShellCommand).toHaveBeenNthCalledWith(1, "pacman -Sy --noconfirm tmate"); + expect(core.info).toHaveBeenNthCalledWith(1, `Web shell: ${customConnectionString}`); + expect(core.info).toHaveBeenNthCalledWith(2, `SSH: ${customConnectionString}`); + expect(core.info).toHaveBeenNthCalledWith(3, "Exiting debugging session because the continue file was created"); + }); + it('should handle the main loop for Windows without dependency installation', async () => { + Object.defineProperty(process, "platform", { + value: "win32" + }) + core.getInput.mockReturnValueOnce("false") + const customConnectionString = "foobar" + execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)) + await run() + expect(execShellCommand).not.toHaveBeenNthCalledWith(1, "pacman -Sy --noconfirm tmate"); expect(core.info).toHaveBeenNthCalledWith(1, `Web shell: ${customConnectionString}`); expect(core.info).toHaveBeenNthCalledWith(2, `SSH: ${customConnectionString}`); expect(core.info).toHaveBeenNthCalledWith(3, "Exiting debugging session because the continue file was created"); @@ -41,7 +54,7 @@ describe('Tmate GitHub integration', () => { Object.defineProperty(process, "platform", { value: "linux" }) - core.getInput.mockReturnValueOnce("true").mockReturnValue("false") + core.getInput.mockReturnValueOnce("true").mockReturnValueOnce("true").mockReturnValueOnce("false") const customConnectionString = "foobar" execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)) await run() @@ -54,7 +67,7 @@ describe('Tmate GitHub integration', () => { Object.defineProperty(process, "platform", { value: "linux" }) - core.getInput.mockReturnValue("false") + core.getInput.mockReturnValueOnce("true").mockReturnValueOnce("false").mockReturnValueOnce("false") const customConnectionString = "foobar" execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)) await run() @@ -63,11 +76,34 @@ describe('Tmate GitHub integration', () => { expect(core.info).toHaveBeenNthCalledWith(2, `SSH: ${customConnectionString}`); expect(core.info).toHaveBeenNthCalledWith(3, "Exiting debugging session because the continue file was created"); }); + it('should be handle the main loop for linux without installing dependencies', async () => { + Object.defineProperty(process, "platform", { + value: "linux" + }) + core.getInput.mockReturnValueOnce("false").mockReturnValueOnce("false") + const customConnectionString = "foobar" + execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)) + await run() + expect(execShellCommand).not.toHaveBeenNthCalledWith(1, "apt-get update") + expect(core.info).toHaveBeenNthCalledWith(1, `Web shell: ${customConnectionString}`); + expect(core.info).toHaveBeenNthCalledWith(2, `SSH: ${customConnectionString}`); + expect(core.info).toHaveBeenNthCalledWith(3, "Exiting debugging session because the continue file was created"); + }); it('should install tmate via brew for darwin', async () => { Object.defineProperty(process, "platform", { value: "darwin" }) + core.getInput.mockReturnValueOnce("true") await run() + expect(core.getInput).toHaveBeenNthCalledWith(1, "install-dependencies") expect(execShellCommand).toHaveBeenNthCalledWith(1, "brew install tmate") }); + it('should not install dependencies for darwin', async () => { + Object.defineProperty(process, "platform", { + value: "darwin" + }) + core.getInput.mockReturnValueOnce("false") + await run() + expect(execShellCommand).not.toHaveBeenNthCalledWith(1, "brew install tmate") + }); });