From 5f95203cead80c02aad86c732037fc0ddaf887d9 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Thu, 4 Mar 2021 12:49:35 +0100 Subject: [PATCH 01/15] Add note about using cargo with private dependencies (#64) * Add note about using cargo with private dependencies * Update doc to mention Windows only * Add alternative workaround * Create extra main section for tips and information regarding different languages/tools Co-authored-by: Matthias Pigulla --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 63fccf8..0a49485 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,38 @@ If the private key is not in the `PEM` format, you will see an `Error loading ke Use `ssh-keygen -p -f path/to/your/key -m pem` to convert your key file to `PEM`, but be sure to make a backup of the file first 😉. +## Additional Information for Particular Tools or Platforms + +If you know that your favorite tool or platform of choice requires extra tweaks or has some caveats when running with SSH, feel free to open a PR to amend this section here. + +### Cargo's (Rust) Private Dependencies on Windows + +If you are using private repositories in your dependencies like this: + +``` +stuff = { git = "ssh://git@github.com/myorg/stuff.git", branch = "main" } +``` + +... you will need to change a configuration in the workflow for Windows machines in order to make cargo able to clone private repositories. + +There are 2 ways you can achieve this: + +1. Add this step once in your job **before** any cargo command: + +``` + - name: Update cargo config to use Git CLI + run: Set-Content -Path $env:USERPROFILE\.cargo\config.toml "[net]`ngit-fetch-with-cli = true" +``` + +This will configure Cargo to use the Git CLI as explained in the [Cargo's documentation](https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli). + +2. Alternatively you can set it to the environment variables for the entire workflow: + +``` +env: + CARGO_NET_GIT_FETCH_WITH_CLI: true +``` + ## What this Action *cannot* do for you The following items are not issues, but beyond what this Action is supposed to do. From 65d1ea3d907cda10122160c201a380dbad15dbd0 Mon Sep 17 00:00:00 2001 From: Shashank Patidar <74622220+shashank11p@users.noreply.github.com> Date: Fri, 5 Mar 2021 04:47:34 +0530 Subject: [PATCH 02/15] Mention that container-based workflows need to have ssh packages installed Co-authored-by: Shashank Patidar <74622220+shashank11p@users.noreply.github.com> --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0a49485..ec02d43 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,10 @@ Use `ssh-keygen -p -f path/to/your/key -m pem` to convert your key file to `PEM` If you know that your favorite tool or platform of choice requires extra tweaks or has some caveats when running with SSH, feel free to open a PR to amend this section here. +### Container-based Workflows + +If you are using this action on container-based workflows, make sure the container has the necessary SSH binaries or package(s) installed. + ### Cargo's (Rust) Private Dependencies on Windows If you are using private repositories in your dependencies like this: From 598c7ea89465463c5f9784314410a9d75362d74f Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 5 Mar 2021 20:17:14 +0000 Subject: [PATCH 03/15] Handle ENOENT exceptions with a graceful message --- dist/index.js | 6 ++++++ index.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dist/index.js b/dist/index.js index ac64344..6b1ea20 100644 --- a/dist/index.js +++ b/dist/index.js @@ -204,6 +204,12 @@ try { }); } catch (error) { + + if (error.code == 'ENOENT') { + console.log(`The '${error.path}' executable could not be found. Please make sure it is on your PATH and/or the necessary packages are installed.`); + console.log(`PATH is set to: ${process.env.PATH}`); + } + core.setFailed(error.message); } diff --git a/index.js b/index.js index cf7b562..199438a 100644 --- a/index.js +++ b/index.js @@ -87,5 +87,11 @@ try { }); } catch (error) { + + if (error.code == 'ENOENT') { + console.log(`The '${error.path}' executable could not be found. Please make sure it is on your PATH and/or the necessary packages are installed.`); + console.log(`PATH is set to: ${process.env.PATH}`); + } + core.setFailed(error.message); } From 795485730f43d85b86350c37087f13cba34660d6 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 10 Mar 2021 08:17:18 +0100 Subject: [PATCH 04/15] Prepare 0.5.1 release --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec02d43..30c266d 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ jobs: ... steps: - actions/checkout@v2 - # Make sure the @v0.5.0 matches the current version of the + # Make sure the @v0.5.1 matches the current version of the # action - - uses: webfactory/ssh-agent@v0.5.0 + - uses: webfactory/ssh-agent@v0.5.1 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - ... other steps @@ -50,7 +50,7 @@ You can set up different keys as different secrets and pass them all to the acti ```yaml # ... contens as before - - uses: webfactory/ssh-agent@v0.5.0 + - uses: webfactory/ssh-agent@v0.5.1 with: ssh-private-key: | ${{ secrets.FIRST_KEY }} From 4b6f4eb000f167422aa955c5a7e848a432931c6f Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 10 Mar 2021 08:19:17 +0100 Subject: [PATCH 05/15] Windows virtual environment: Use SSH binaries from the Git suite (#63) * Use SSH binaries from the Git suite * Try to kill the ssh-agent upon action termination on Windows as well --- .github/workflows/demo.yml | 82 ++++++++++++++------------------ cleanup.js | 10 ++-- dist/cleanup.js | 35 ++++++++++++-- dist/index.js | 96 ++++++++++++++++++++++---------------- index.js | 71 ++++++++++++---------------- paths.js | 18 +++++++ 6 files changed, 174 insertions(+), 138 deletions(-) create mode 100644 paths.js diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index c3bb009..9d8fad0 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -1,60 +1,46 @@ -on: [push, pull_request] +on: [ push, pull_request ] jobs: - single_key_demo: - strategy: - matrix: - os: [ubuntu-latest, macOS-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - - name: Setup key - uses: ./ - with: - ssh-private-key: | - ${{ secrets.DEMO_KEY }} - ${{ secrets.DEMO_KEY_2 }} - - multiple_keys_demo: + deployment_keys_demo: strategy: + fail-fast: false matrix: - os: [ubuntu-latest, macOS-latest] + os: [ ubuntu-latest, macOS-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - name: Setup key - uses: ./ - with: - ssh-private-key: ${{ secrets.DEMO_KEY }} + - uses: actions/checkout@v2 + - name: Setup key + uses: ./ + with: + ssh-private-key: | + ${{ secrets.MPDUDE_TEST_1_DEPLOY_KEY }} + ${{ secrets.MPDUDE_TEST_2_DEPLOY_KEY }} + - run: | + git clone https://github.com/mpdude/test-1.git test-1-http + git clone git@github.com:mpdude/test-1.git test-1-git + git clone ssh://git@github.com/mpdude/test-1.git test-1-git-ssh + git clone https://github.com/mpdude/test-2.git test-2-http + git clone git@github.com:mpdude/test-2.git test-2-git + git clone ssh://git@github.com/mpdude/test-2.git test-2-git-ssh docker_demo: - runs-on: ubuntu-latest + runs-on: ubuntu-latest container: image: ubuntu:latest steps: - - uses: actions/checkout@v2 - - run: apt update && apt install -y openssh-client - - name: Setup key - uses: ./ - with: - ssh-private-key: | - ${{ secrets.DEMO_KEY }} - ${{ secrets.DEMO_KEY_2 }} + - uses: actions/checkout@v2 + - run: apt update && apt install -y openssh-client git + - name: Setup key + uses: ./ + with: + ssh-private-key: | + ${{ secrets.MPDUDE_TEST_1_DEPLOY_KEY }} + ${{ secrets.MPDUDE_TEST_2_DEPLOY_KEY }} + - run: | + git clone https://github.com/mpdude/test-1.git test-1-http + git clone git@github.com:mpdude/test-1.git test-1-git + git clone ssh://git@github.com/mpdude/test-1.git test-1-git-ssh + git clone https://github.com/mpdude/test-2.git test-2-http + git clone git@github.com:mpdude/test-2.git test-2-git + git clone ssh://git@github.com/mpdude/test-2.git test-2-git-ssh - deployment_keys_demo: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup key - uses: ./ - with: - ssh-private-key: | - ${{ secrets.MPDUDE_TEST_1_DEPLOY_KEY }} - ${{ secrets.MPDUDE_TEST_2_DEPLOY_KEY }} - - run: | - git clone https://github.com/mpdude/test-1.git test-1-http - git clone git@github.com:mpdude/test-1.git test-1-git - git clone ssh://git@github.com/mpdude/test-1.git test-1-git-ssh - git clone https://github.com/mpdude/test-2.git test-2-http - git clone git@github.com:mpdude/test-2.git test-2-git - git clone ssh://git@github.com/mpdude/test-2.git test-2-git-ssh diff --git a/cleanup.js b/cleanup.js index f90cddd..529fbe8 100644 --- a/cleanup.js +++ b/cleanup.js @@ -1,10 +1,12 @@ -const core = require('@actions/core') -const { execSync } = require('child_process') +const core = require('@actions/core'); +const { execSync } = require('child_process'); +const { sshAgent } = require('./paths.js'); try { // Kill the started SSH agent - console.log('Stopping SSH agent') - execSync('kill ${SSH_AGENT_PID}', { stdio: 'inherit' }) + console.log('Stopping SSH agent'); + execSync(sshAgent, ['-k'], { stdio: 'inherit' }); + } catch (error) { console.log(error.message); console.log('Error stopping the SSH agent, proceeding anyway'); diff --git a/dist/cleanup.js b/dist/cleanup.js index c8081be..49024b4 100644 --- a/dist/cleanup.js +++ b/dist/cleanup.js @@ -122,13 +122,15 @@ module.exports = require("child_process"); /***/ 175: /***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { -const core = __webpack_require__(470) -const { execSync } = __webpack_require__(129) +const core = __webpack_require__(470); +const { execSync } = __webpack_require__(129); +const { sshAgent } = __webpack_require__(972); try { // Kill the started SSH agent - console.log('Stopping SSH agent') - execSync('kill ${SSH_AGENT_PID}', { stdio: 'inherit' }) + console.log('Stopping SSH agent'); + execSync(sshAgent, ['-k'], { stdio: 'inherit' }); + } catch (error) { console.log(error.message); console.log('Error stopping the SSH agent, proceeding anyway'); @@ -480,6 +482,31 @@ module.exports = require("path"); module.exports = require("fs"); +/***/ }), + +/***/ 972: +/***/ (function(module, __unusedexports, __webpack_require__) { + +const os = __webpack_require__(87); + +module.exports = (process.env['OS'] != 'Windows_NT') ? { + + // Use getent() system call, since this is what ssh does; makes a difference in Docker-based + // Action runs, where $HOME is different from the pwent + home: os.userInfo().homedir, + sshAgent: 'ssh-agent', + sshAdd: 'ssh-add' + +} : { + + home: os.homedir(), + sshAgent: 'c://progra~1//git//usr//bin//ssh-agent.exe', + sshAdd: 'c://progra~1//git//usr//bin//ssh-add.exe' + +}; + + + /***/ }) /******/ }); \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 6b1ea20..f75d5b2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -118,8 +118,8 @@ exports.issueCommand = issueCommand; const core = __webpack_require__(470); const child_process = __webpack_require__(129); const fs = __webpack_require__(747); -const os = __webpack_require__(87); const crypto = __webpack_require__(417); +const { home, sshAgent, sshAdd } = __webpack_require__(972); try { const privateKey = core.getInput('ssh-private-key'); @@ -130,77 +130,66 @@ try { return; } - var home; - - if (process.env['OS'] == 'Windows_NT') { - console.log('Preparing ssh-agent service on Windows'); - child_process.execSync('sc config ssh-agent start=demand', { stdio: 'inherit' }); - - home = os.homedir(); - } else { - // Use getent() system call, since this is what ssh does; makes a difference in Docker-based - // Action runs, where $HOME is different from the pwent - var { homedir: home } = os.userInfo(); - } - const homeSsh = home + '/.ssh'; console.log(`Adding GitHub.com keys to ${homeSsh}/known_hosts`); + fs.mkdirSync(homeSsh, { recursive: true }); fs.appendFileSync(`${homeSsh}/known_hosts`, '\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n'); fs.appendFileSync(`${homeSsh}/known_hosts`, '\ngithub.com ssh-dss AAAAB3NzaC1kc3MAAACBANGFW2P9xlGU3zWrymJgI/lKo//ZW2WfVtmbsUZJ5uyKArtlQOT2+WRhcg4979aFxgKdcsqAYW3/LS1T2km3jYW/vr4Uzn+dXWODVk5VlUiZ1HFOHf6s6ITcZvjvdbp6ZbpM+DuJT7Bw+h5Fx8Qt8I16oCZYmAPJRtu46o9C2zk1AAAAFQC4gdFGcSbp5Gr0Wd5Ay/jtcldMewAAAIATTgn4sY4Nem/FQE+XJlyUQptPWMem5fwOcWtSXiTKaaN0lkk2p2snz+EJvAGXGq9dTSWHyLJSM2W6ZdQDqWJ1k+cL8CARAqL+UMwF84CR0m3hj+wtVGD/J4G5kW2DBAf4/bqzP4469lT+dF2FRQ2L9JKXrCWcnhMtJUvua8dvnwAAAIB6C4nQfAA7x8oLta6tT+oCk2WQcydNsyugE8vLrHlogoWEicla6cWPk7oXSspbzUcfkjN3Qa6e74PhRkc7JdSdAlFzU3m7LMkXo1MHgkqNX8glxWNVqBSc0YRdbFdTkL0C6gtpklilhvuHQCdbgB3LBAikcRkDp+FCVkUgPC/7Rw==\n'); console.log("Starting ssh-agent"); + const authSock = core.getInput('ssh-auth-sock'); - let sshAgentOutput = '' - if (authSock && authSock.length > 0) { - sshAgentOutput = child_process.execFileSync('ssh-agent', ['-a', authSock]); - } else { - sshAgentOutput = child_process.execFileSync('ssh-agent') - } + const sshAgentArgs = (authSock && authSock.length > 0) ? ['-a', authSock] : []; // Extract auth socket path and agent pid and set them as job variables - const lines = sshAgentOutput.toString().split("\n") - for (const lineNumber in lines) { - const matches = /^(SSH_AUTH_SOCK|SSH_AGENT_PID)=(.*); export \1/.exec(lines[lineNumber]) + child_process.execFileSync(sshAgent, sshAgentArgs).toString().split("\n").forEach(function(line) { + const matches = /^(SSH_AUTH_SOCK|SSH_AGENT_PID)=(.*); export \1/.exec(line); + if (matches && matches.length > 0) { + // This will also set process.env accordingly, so changes take effect for this script core.exportVariable(matches[1], matches[2]) + console.log(`${matches[1]}=${matches[2]}`); } - } + }); + + console.log("Adding private key(s) to agent"); - console.log("Adding private key to agent"); privateKey.split(/(?=-----BEGIN)/).forEach(function(key) { - child_process.execSync('ssh-add -', { input: key.trim() + "\n" }); + child_process.execFileSync(sshAdd, ['-'], { input: key.trim() + "\n" }); }); - console.log("Keys added:"); - child_process.execSync('ssh-add -l', { stdio: 'inherit' }); + console.log("Key(s) added:"); + + child_process.execFileSync(sshAdd, ['-l'], { stdio: 'inherit' }); + + console.log('Configuring deployment key(s)'); - child_process.execFileSync('ssh-add', ['-L']).toString().split(/\r?\n/).forEach(function(key) { - let parts = key.match(/\bgithub.com[:/](.*)(?:\.git)?\b/); + child_process.execFileSync(sshAdd, ['-L']).toString().split(/\r?\n/).forEach(function(key) { + const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/); - if (parts == null) { + if (!parts) { return; } - let ownerAndRepo = parts[1]; - let sha256 = crypto.createHash('sha256').update(key).digest('hex'); + const sha256 = crypto.createHash('sha256').update(key).digest('hex'); + const ownerAndRepo = parts[1].replace(/\.git$/, ''); - fs.writeFileSync(`${homeSsh}/${sha256}`, key + "\n", { mode: '600' }); + fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' }); - child_process.execSync(`git config --global --replace-all url."git@${sha256}:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); - child_process.execSync(`git config --global --add url."git@${sha256}:${ownerAndRepo}".insteadOf "git@github.com:${ownerAndRepo}"`); - child_process.execSync(`git config --global --add url."git@${sha256}:${ownerAndRepo}".insteadOf "ssh://git@github.com/${ownerAndRepo}"`); + child_process.execSync(`git config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); + child_process.execSync(`git config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "git@github.com:${ownerAndRepo}"`); + child_process.execSync(`git config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://git@github.com/${ownerAndRepo}"`); - let sshConfig = `\nHost ${sha256}\n` + const sshConfig = `\nHost key-${sha256}.github.com\n` + ` HostName github.com\n` - + ` User git\n` - + ` IdentityFile ${homeSsh}/${sha256}\n` + + ` IdentityFile ${homeSsh}/key-${sha256}\n` + ` IdentitiesOnly yes\n`; fs.appendFileSync(`${homeSsh}/config`, sshConfig); - console.log(`Added deploy-key mapping: Use key "${key}" for GitHub repository ${ownerAndRepo}`); + console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); }); } catch (error) { @@ -573,6 +562,31 @@ module.exports = require("path"); module.exports = require("fs"); +/***/ }), + +/***/ 972: +/***/ (function(module, __unusedexports, __webpack_require__) { + +const os = __webpack_require__(87); + +module.exports = (process.env['OS'] != 'Windows_NT') ? { + + // Use getent() system call, since this is what ssh does; makes a difference in Docker-based + // Action runs, where $HOME is different from the pwent + home: os.userInfo().homedir, + sshAgent: 'ssh-agent', + sshAdd: 'ssh-add' + +} : { + + home: os.homedir(), + sshAgent: 'c://progra~1//git//usr//bin//ssh-agent.exe', + sshAdd: 'c://progra~1//git//usr//bin//ssh-add.exe' + +}; + + + /***/ }) /******/ }); \ No newline at end of file diff --git a/index.js b/index.js index 199438a..5243e61 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,8 @@ const core = require('@actions/core'); const child_process = require('child_process'); const fs = require('fs'); -const os = require('os'); const crypto = require('crypto'); +const { home, sshAgent, sshAdd } = require('./paths.js'); try { const privateKey = core.getInput('ssh-private-key'); @@ -13,77 +13,66 @@ try { return; } - var home; - - if (process.env['OS'] == 'Windows_NT') { - console.log('Preparing ssh-agent service on Windows'); - child_process.execSync('sc config ssh-agent start=demand', { stdio: 'inherit' }); - - home = os.homedir(); - } else { - // Use getent() system call, since this is what ssh does; makes a difference in Docker-based - // Action runs, where $HOME is different from the pwent - var { homedir: home } = os.userInfo(); - } - const homeSsh = home + '/.ssh'; console.log(`Adding GitHub.com keys to ${homeSsh}/known_hosts`); + fs.mkdirSync(homeSsh, { recursive: true }); fs.appendFileSync(`${homeSsh}/known_hosts`, '\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n'); fs.appendFileSync(`${homeSsh}/known_hosts`, '\ngithub.com ssh-dss AAAAB3NzaC1kc3MAAACBANGFW2P9xlGU3zWrymJgI/lKo//ZW2WfVtmbsUZJ5uyKArtlQOT2+WRhcg4979aFxgKdcsqAYW3/LS1T2km3jYW/vr4Uzn+dXWODVk5VlUiZ1HFOHf6s6ITcZvjvdbp6ZbpM+DuJT7Bw+h5Fx8Qt8I16oCZYmAPJRtu46o9C2zk1AAAAFQC4gdFGcSbp5Gr0Wd5Ay/jtcldMewAAAIATTgn4sY4Nem/FQE+XJlyUQptPWMem5fwOcWtSXiTKaaN0lkk2p2snz+EJvAGXGq9dTSWHyLJSM2W6ZdQDqWJ1k+cL8CARAqL+UMwF84CR0m3hj+wtVGD/J4G5kW2DBAf4/bqzP4469lT+dF2FRQ2L9JKXrCWcnhMtJUvua8dvnwAAAIB6C4nQfAA7x8oLta6tT+oCk2WQcydNsyugE8vLrHlogoWEicla6cWPk7oXSspbzUcfkjN3Qa6e74PhRkc7JdSdAlFzU3m7LMkXo1MHgkqNX8glxWNVqBSc0YRdbFdTkL0C6gtpklilhvuHQCdbgB3LBAikcRkDp+FCVkUgPC/7Rw==\n'); console.log("Starting ssh-agent"); + const authSock = core.getInput('ssh-auth-sock'); - let sshAgentOutput = '' - if (authSock && authSock.length > 0) { - sshAgentOutput = child_process.execFileSync('ssh-agent', ['-a', authSock]); - } else { - sshAgentOutput = child_process.execFileSync('ssh-agent') - } + const sshAgentArgs = (authSock && authSock.length > 0) ? ['-a', authSock] : []; // Extract auth socket path and agent pid and set them as job variables - const lines = sshAgentOutput.toString().split("\n") - for (const lineNumber in lines) { - const matches = /^(SSH_AUTH_SOCK|SSH_AGENT_PID)=(.*); export \1/.exec(lines[lineNumber]) + child_process.execFileSync(sshAgent, sshAgentArgs).toString().split("\n").forEach(function(line) { + const matches = /^(SSH_AUTH_SOCK|SSH_AGENT_PID)=(.*); export \1/.exec(line); + if (matches && matches.length > 0) { + // This will also set process.env accordingly, so changes take effect for this script core.exportVariable(matches[1], matches[2]) + console.log(`${matches[1]}=${matches[2]}`); } - } + }); + + console.log("Adding private key(s) to agent"); - console.log("Adding private key to agent"); privateKey.split(/(?=-----BEGIN)/).forEach(function(key) { - child_process.execSync('ssh-add -', { input: key.trim() + "\n" }); + child_process.execFileSync(sshAdd, ['-'], { input: key.trim() + "\n" }); }); - console.log("Keys added:"); - child_process.execSync('ssh-add -l', { stdio: 'inherit' }); + console.log("Key(s) added:"); + + child_process.execFileSync(sshAdd, ['-l'], { stdio: 'inherit' }); + + console.log('Configuring deployment key(s)'); - child_process.execFileSync('ssh-add', ['-L']).toString().split(/\r?\n/).forEach(function(key) { - let parts = key.match(/\bgithub.com[:/](.*)(?:\.git)?\b/); + child_process.execFileSync(sshAdd, ['-L']).toString().split(/\r?\n/).forEach(function(key) { + const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/); - if (parts == null) { + if (!parts) { return; } - let ownerAndRepo = parts[1]; - let sha256 = crypto.createHash('sha256').update(key).digest('hex'); + const sha256 = crypto.createHash('sha256').update(key).digest('hex'); + const ownerAndRepo = parts[1].replace(/\.git$/, ''); - fs.writeFileSync(`${homeSsh}/${sha256}`, key + "\n", { mode: '600' }); + fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' }); - child_process.execSync(`git config --global --replace-all url."git@${sha256}:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); - child_process.execSync(`git config --global --add url."git@${sha256}:${ownerAndRepo}".insteadOf "git@github.com:${ownerAndRepo}"`); - child_process.execSync(`git config --global --add url."git@${sha256}:${ownerAndRepo}".insteadOf "ssh://git@github.com/${ownerAndRepo}"`); + child_process.execSync(`git config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); + child_process.execSync(`git config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "git@github.com:${ownerAndRepo}"`); + child_process.execSync(`git config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://git@github.com/${ownerAndRepo}"`); - let sshConfig = `\nHost ${sha256}\n` + const sshConfig = `\nHost key-${sha256}.github.com\n` + ` HostName github.com\n` - + ` User git\n` - + ` IdentityFile ${homeSsh}/${sha256}\n` + + ` IdentityFile ${homeSsh}/key-${sha256}\n` + ` IdentitiesOnly yes\n`; fs.appendFileSync(`${homeSsh}/config`, sshConfig); - console.log(`Added deploy-key mapping: Use key "${key}" for GitHub repository ${ownerAndRepo}`); + console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); }); } catch (error) { diff --git a/paths.js b/paths.js new file mode 100644 index 0000000..8ee7afd --- /dev/null +++ b/paths.js @@ -0,0 +1,18 @@ +const os = require('os'); + +module.exports = (process.env['OS'] != 'Windows_NT') ? { + + // Use getent() system call, since this is what ssh does; makes a difference in Docker-based + // Action runs, where $HOME is different from the pwent + home: os.userInfo().homedir, + sshAgent: 'ssh-agent', + sshAdd: 'ssh-add' + +} : { + + home: os.homedir(), + sshAgent: 'c://progra~1//git//usr//bin//ssh-agent.exe', + sshAdd: 'c://progra~1//git//usr//bin//ssh-add.exe' + +}; + From 4681241867865c064d220082c7b6cbe128c16171 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 17 Mar 2021 18:27:52 +0000 Subject: [PATCH 06/15] Use case-insensitive regex matching when scanning key comments Resolves #68, closes #70, closes #71. Co-authored-by: Sean Killeen --- dist/index.js | 2 +- index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index f75d5b2..883d7c7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -167,7 +167,7 @@ try { console.log('Configuring deployment key(s)'); child_process.execFileSync(sshAdd, ['-L']).toString().split(/\r?\n/).forEach(function(key) { - const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/); + const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); if (!parts) { return; diff --git a/index.js b/index.js index 5243e61..6c8d969 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,7 @@ try { console.log('Configuring deployment key(s)'); child_process.execFileSync(sshAdd, ['-L']).toString().split(/\r?\n/).forEach(function(key) { - const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/); + const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); if (!parts) { return; From aed5400f20865ee7ae916e3283597c9a56577778 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 17 Mar 2021 18:50:49 +0000 Subject: [PATCH 07/15] Log when a key is _not_ used as a deploy key Resolves #69. Co-authored-by: Sean Killeen --- dist/index.js | 2 ++ index.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dist/index.js b/dist/index.js index 883d7c7..6dbf026 100644 --- a/dist/index.js +++ b/dist/index.js @@ -170,6 +170,8 @@ try { const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); if (!parts) { + console.log(`Comment for key '${key}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); + return; } diff --git a/index.js b/index.js index 6c8d969..1a4835e 100644 --- a/index.js +++ b/index.js @@ -53,6 +53,8 @@ try { const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); if (!parts) { + console.log(`Comment for key '${key}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); + return; } From cb8b21017acfd319f0f4eb320ae495f22c36d0a7 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 7 Apr 2021 12:30:27 +0200 Subject: [PATCH 08/15] Update version numbers in README for the next bugfix release --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 30c266d..41d5e4b 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ jobs: ... steps: - actions/checkout@v2 - # Make sure the @v0.5.1 matches the current version of the + # Make sure the @v0.5.2 matches the current version of the # action - - uses: webfactory/ssh-agent@v0.5.1 + - uses: webfactory/ssh-agent@v0.5.2 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - ... other steps @@ -50,7 +50,7 @@ You can set up different keys as different secrets and pass them all to the acti ```yaml # ... contens as before - - uses: webfactory/ssh-agent@v0.5.1 + - uses: webfactory/ssh-agent@v0.5.2 with: ssh-private-key: | ${{ secrets.FIRST_KEY }} From 98f76b1158e862ff1ed07602f86cb11dd3716345 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 27 May 2021 20:05:28 +0000 Subject: [PATCH 09/15] Give an example of how to add a key comment Co-authored-by: rr-james-hickman --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41d5e4b..452b122 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ When using **Github deploy keys**, GitHub servers will accept the _first_ known To support picking the right key in this use case, this action scans _key comments_ and will set up extra Git and SSH configuration to make things work. -1. When creating the deploy key for a repository like `git@github.com:owner/repo.git` or `https://github.com/owner/repo`, put that URL into the key comment. +1. When creating the deploy key for a repository like `git@github.com:owner/repo.git` or `https://github.com/owner/repo`, put that URL into the key comment. (Hint: Try `ssh-keygen ... -C "git@github.com:owner/repo.git"`.) 2. After keys have been added to the agent, this action will scan the key comments. 3. For key comments containing such URLs, a Git config setting is written that uses [`url..insteadof`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-urlltbasegtinsteadOf). It will redirect `git` requests to URLs starting with either `https://github.com/owner/repo` or `git@github.com:owner/repo` to a fake hostname/URL like `git@...some.hash...:owner/repo`. 4. An SSH configuration section is generated that applies to the fake hostname. It will map the SSH connection back to `github.com`, while at the same time pointing SSH to a file containing the appropriate key's public part. That will make SSH use the right key when connecting to GitHub.com. From 8569bedfe095bb3c5b3c636aab03add3ec45cf27 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 27 May 2021 20:11:56 +0000 Subject: [PATCH 10/15] Mention "-scmProvider system" for XCode builds/Swift Package Manager Co-authored-by: rr-james-hickman --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 452b122..ed299e1 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,10 @@ env: CARGO_NET_GIT_FETCH_WITH_CLI: true ``` +### Using Deploy Keys with Swift Package Manager + +`xcodebuild` by default uses Xcode's built-it Git tooling. If you want to use GitHub Deploy Keys as supported by this action, however, that version of Git will lack the necessary URL remapping. In this case, pass `-scmProvider system` to the `xcodebuild` command, as mentioned in [Apple's documentation](https://developer.apple.com/documentation/swift_packages/building_swift_packages_or_apps_that_use_them_in_continuous_integration_workflows#3680255). + ## What this Action *cannot* do for you The following items are not issues, but beyond what this Action is supposed to do. From 515d164e78d902edeb5c201b2090a14a17549447 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 2 Jun 2021 22:15:05 +0200 Subject: [PATCH 11/15] Run cleanup (post) step also on failure (#79) According to https://github.com/actions/runner/issues/987, this should run the post step (cleanup.js) also when a workflow fails. Probably most important on self-hosted runners that are not ephemeral, to terminate SSH agents from failed jobs as well. --- action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/action.yml b/action.yml index e43c44f..16aaba6 100644 --- a/action.yml +++ b/action.yml @@ -10,6 +10,7 @@ runs: using: 'node12' main: 'dist/index.js' post: 'dist/cleanup.js' + post-if: 'always()' branding: icon: loader color: 'yellow' From 81d965f2bd14ec88844371f6b5cd8e6ab8071e83 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 3 Jun 2021 23:33:40 +0200 Subject: [PATCH 12/15] Tix a fypo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed299e1..b20a09b 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ env: ### Using Deploy Keys with Swift Package Manager -`xcodebuild` by default uses Xcode's built-it Git tooling. If you want to use GitHub Deploy Keys as supported by this action, however, that version of Git will lack the necessary URL remapping. In this case, pass `-scmProvider system` to the `xcodebuild` command, as mentioned in [Apple's documentation](https://developer.apple.com/documentation/swift_packages/building_swift_packages_or_apps_that_use_them_in_continuous_integration_workflows#3680255). +`xcodebuild` by default uses Xcode's built-in Git tooling. If you want to use GitHub Deploy Keys as supported by this action, however, that version of Git will lack the necessary URL remapping. In this case, pass `-scmProvider system` to the `xcodebuild` command, as mentioned in [Apple's documentation](https://developer.apple.com/documentation/swift_packages/building_swift_packages_or_apps_that_use_them_in_continuous_integration_workflows#3680255). ## What this Action *cannot* do for you From a45226bfaf7df5d2db6d1a46d0a280d9185a4402 Mon Sep 17 00:00:00 2001 From: Maciej Pasternacki <52241383+maciejp-ro@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:17:22 +0200 Subject: [PATCH 13/15] Use execFileSync to clean up (#80) execSync just started a second ssh-agent. `['-k']` argument was treated as options, it didn't have `stdio` set, so stdio was piped and returned (and ignored). --- cleanup.js | 5 ++--- dist/cleanup.js | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cleanup.js b/cleanup.js index 529fbe8..34b2bbc 100644 --- a/cleanup.js +++ b/cleanup.js @@ -1,12 +1,11 @@ const core = require('@actions/core'); -const { execSync } = require('child_process'); +const { execFileSync } = require('child_process'); const { sshAgent } = require('./paths.js'); try { // Kill the started SSH agent console.log('Stopping SSH agent'); - execSync(sshAgent, ['-k'], { stdio: 'inherit' }); - + execFileSync(sshAgent, ['-k'], { stdio: 'inherit' }); } catch (error) { console.log(error.message); console.log('Error stopping the SSH agent, proceeding anyway'); diff --git a/dist/cleanup.js b/dist/cleanup.js index 49024b4..38a3d56 100644 --- a/dist/cleanup.js +++ b/dist/cleanup.js @@ -123,14 +123,13 @@ module.exports = require("child_process"); /***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { const core = __webpack_require__(470); -const { execSync } = __webpack_require__(129); +const { execFileSync } = __webpack_require__(129); const { sshAgent } = __webpack_require__(972); try { // Kill the started SSH agent console.log('Stopping SSH agent'); - execSync(sshAgent, ['-k'], { stdio: 'inherit' }); - + execFileSync(sshAgent, ['-k'], { stdio: 'inherit' }); } catch (error) { console.log(error.message); console.log('Error stopping the SSH agent, proceeding anyway'); From 5f066a372ec13036ab7cb9a8adf18c936f8d2043 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 11 Jun 2021 15:18:45 +0200 Subject: [PATCH 14/15] Prepare a 0.5.3 release --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b20a09b..87c63a9 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ jobs: ... steps: - actions/checkout@v2 - # Make sure the @v0.5.2 matches the current version of the + # Make sure the @v0.5.3 matches the current version of the # action - - uses: webfactory/ssh-agent@v0.5.2 + - uses: webfactory/ssh-agent@v0.5.3 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - ... other steps @@ -50,7 +50,7 @@ You can set up different keys as different secrets and pass them all to the acti ```yaml # ... contens as before - - uses: webfactory/ssh-agent@v0.5.2 + - uses: webfactory/ssh-agent@v0.5.3 with: ssh-private-key: | ${{ secrets.FIRST_KEY }} From 0d1843b70e4bcba4b643a8a424c68b31173ae502 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 24 Jun 2021 13:36:50 +0200 Subject: [PATCH 15/15] Test "go get" --- .github/workflows/demo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index 9d8fad0..51e1cd0 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -22,6 +22,7 @@ jobs: git clone https://github.com/mpdude/test-2.git test-2-http git clone git@github.com:mpdude/test-2.git test-2-git git clone ssh://git@github.com/mpdude/test-2.git test-2-git-ssh + go get -v github.com/mpdude/test-2 docker_demo: runs-on: ubuntu-latest