-
Couldn't load subscription status.
- Fork 0
feat: replace ssh-private-key with ssh-private-keys and expected input is json with array of {name, key} entries. #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,46 +1,37 @@ | ||
| on: [ push, pull_request ] | ||
|
|
||
| jobs: | ||
| deployment_keys_demo: | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [ ubuntu-latest, macOS-latest, windows-latest ] | ||
| runs-on: ${{ matrix.os }} | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - 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 [email protected]:mpdude/test-1.git test-1-git | ||
| git clone ssh://[email protected]/mpdude/test-1.git test-1-git-ssh | ||
| git clone https://github.com/mpdude/test-2.git test-2-http | ||
| git clone [email protected]:mpdude/test-2.git test-2-git | ||
| git clone ssh://[email protected]/mpdude/test-2.git test-2-git-ssh | ||
|
|
||
| docker_demo: | ||
| runs-on: ubuntu-latest | ||
| container: | ||
| image: ubuntu:latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - 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 [email protected]:mpdude/test-1.git test-1-git | ||
| git clone ssh://[email protected]/mpdude/test-1.git test-1-git-ssh | ||
| git clone https://github.com/mpdude/test-2.git test-2-http | ||
| git clone [email protected]:mpdude/test-2.git test-2-git | ||
| git clone ssh://[email protected]/mpdude/test-2.git test-2-git-ssh | ||
|
|
||
| vmo_deployment_keys_demo: | ||
| runs-on: ubuntu-latest | ||
| container: | ||
| image: ubuntu:latest | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - run: apt update && apt install -y openssh-client git curl | ||
| - if: ${{ env.ACT }} | ||
| name: Hack container for local development | ||
| run: | | ||
| curl -fsSL https://deb.nodesource.com/setup_16.x | bash -s | ||
| apt-get install -y nodejs | ||
| - name: Setup node | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: '16.x' | ||
| - name: Setup key | ||
| uses: ./ | ||
| with: | ||
| ssh-private-keys: | | ||
| [ | ||
| { | ||
| "name": "[email protected]:vaimo/vsf2_ext-lru-cache-driver.git", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this use yaml instead of text? (i.e. consistent with the rest of the file and validated by the yaml parser, before the action even starts running?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. github action doesn't support it. only accepts text as input from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. The format feels quite verbose to me. Would the follow perhaps be easier/nicer (use repo name as key)? ssh-private-keys: |
{
"[email protected]:vaimo/vsf2_ext-lru-cache-driver.git": "${{ secrets.TEST_1_DEPLOY_KEY }}",
"[email protected]:mpdude/test-2.git": "${{ secrets.TEST_2_DEPLOY_KEY }}"
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will think of that, doesn't hurt to refactor it to that way 👍🏻 |
||
| "key": "${{ secrets.TEST_1_DEPLOY_KEY }}" | ||
| }, | ||
| { | ||
| "name": "[email protected]:mpdude/test-2.git", | ||
| "key": "${{ secrets.TEST_2_DEPLOY_KEY }}" | ||
| } | ||
| ] | ||
| - run: | | ||
| git clone https://github.com/vaimo/vsf2_ext-lru-cache-driver.git test-1-http | ||
| git clone [email protected]:vaimo/vsf2_ext-lru-cache-driver.git test-1-git | ||
| git clone ssh://[email protected]/vaimo/vsf2_ext-lru-cache-driver.git test-1-git-ssh | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,8 +39,14 @@ jobs: | |
| # Make sure the @v0.6.0 matches the current version of the | ||
| # action | ||
| - uses: webfactory/[email protected] | ||
| with: | ||
| ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} | ||
| with: | ||
| ssh-private-keys: | | ||
| [ | ||
| { | ||
| "name": "[email protected]:vendor/repo-1.git", | ||
| "key": "${{ secrets.SSH_PRIVATE_KEY }}" | ||
| } | ||
| ] | ||
| - ... other steps | ||
| ``` | ||
| 5. If, for some reason, you need to change the location of the SSH agent socket, you can use the `ssh-auth-sock` input to provide a path. | ||
|
|
@@ -55,10 +61,21 @@ You can set up different keys as different secrets and pass them all to the acti | |
| # ... contens as before | ||
| - uses: webfactory/[email protected] | ||
| with: | ||
| ssh-private-key: | | ||
| ${{ secrets.FIRST_KEY }} | ||
| ${{ secrets.NEXT_KEY }} | ||
| ${{ secrets.ANOTHER_KEY }} | ||
| ssh-private-keys: | | ||
| [ | ||
| { | ||
| "name": "[email protected]:vendor/repo-1.git", | ||
| "key": "${{ secrets.FIRST_KEY }}" | ||
| }, | ||
| { | ||
| "name": "[email protected]:vendor/repo-2.git", | ||
| "key": "${{ secrets.NEXT_KEY }}" | ||
| }, | ||
| { | ||
| "name": "[email protected]:vendor/repo-3.git", | ||
| "key": "${{ secrets.ANOTHER_KEY }}" | ||
| } | ||
| ] | ||
| ``` | ||
|
|
||
| The `ssh-agent` will load all of the keys and try each one in order when establishing SSH connections. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -322,18 +322,20 @@ const core = __webpack_require__(470); | |
| const child_process = __webpack_require__(129); | ||
| const fs = __webpack_require__(747); | ||
| const crypto = __webpack_require__(417); | ||
| const { homePath, sshAgentCmd, sshAddCmd, gitCmd } = __webpack_require__(972); | ||
| const { homePath, sshAgentCmd, sshAddCmd, sshKeyGenCmd, gitCmd } = __webpack_require__(972); | ||
|
|
||
| try { | ||
| const privateKey = core.getInput('ssh-private-key'); | ||
| const privateKeys = core.getInput('ssh-private-keys'); | ||
| const logPublicKey = core.getBooleanInput('log-public-key', {default: true}); | ||
|
|
||
| if (!privateKey) { | ||
| core.setFailed("The ssh-private-key argument is empty. Maybe the secret has not been configured, or you are using a wrong secret name in your workflow file."); | ||
| if (!privateKeys) { | ||
| core.setFailed("The ssh-private-keys Array{name, key} argument is empty. Maybe the secret has not been configured, or you are using a wrong secret name in your workflow file."); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| const privateKeysData = JSON.parse(privateKeys.replaceAll("\n", "")) | ||
|
|
||
| const homeSsh = homePath + '/.ssh'; | ||
|
|
||
| console.log(`Adding GitHub.com keys to ${homeSsh}/known_hosts`); | ||
|
|
@@ -359,47 +361,58 @@ try { | |
| } | ||
| }); | ||
|
|
||
| console.log("Adding private key(s) to agent"); | ||
|
|
||
| privateKey.split(/(?=-----BEGIN)/).forEach(function(key) { | ||
| child_process.execFileSync(sshAddCmd, ['-'], { input: key.trim() + "\n" }); | ||
| }); | ||
| console.log("Adding private key(s) to agent and Configuring deployment key(s)"); | ||
|
|
||
| console.log("Key(s) added:"); | ||
| privateKeysData.forEach(async ({ name, key }) => { | ||
| const repoName = name.trim(); | ||
| let privateKey = key.trim(); | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-l'], { stdio: 'inherit' }); | ||
| privateKey = privateKey.replace(/(KEY-----)(...)/, '$1\n$2') | ||
| .replace(/(...)(-----END )/, '$1\n$2') + "\n" | ||
|
|
||
| console.log('Configuring deployment key(s)'); | ||
| child_process.execFileSync(sshAddCmd, ['-'], { input: privateKey }); | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-L']).toString().trim().split(/\r?\n/).forEach(function(key) { | ||
| const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); | ||
| const sha256 = crypto.createHash('sha256').update(privateKey).digest('hex'); | ||
| const filename = `${homeSsh}/key-${sha256}` | ||
|
|
||
| if (!parts) { | ||
| if (logPublicKey) { | ||
| console.log(`Comment for (public) key '${key}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); | ||
| await fs.writeFile(filename, privateKey, { }, (err) => { | ||
| if (err) { | ||
| console.log(err) | ||
| return | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| const sha256 = crypto.createHash('sha256').update(key).digest('hex'); | ||
| const ownerAndRepo = parts[1].replace(/\.git$/, ''); | ||
| fs.chmodSync(filename, '600') | ||
|
|
||
| fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' }); | ||
| const parts = repoName.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); | ||
|
|
||
| child_process.execSync(`${gitCmd} config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "[email protected]:${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://[email protected]/${ownerAndRepo}"`); | ||
| if (!parts) { | ||
| if (logPublicKey) { | ||
| console.log(`Comment for name '${repoName}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); | ||
| } | ||
|
|
||
| const sshConfig = `\nHost key-${sha256}.github.com\n` | ||
| return; | ||
| } | ||
|
|
||
| const ownerAndRepo = parts[1].replace(/\.git$/, ''); | ||
|
|
||
| child_process.execSync(`${gitCmd} config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "[email protected]:${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://[email protected]/${ownerAndRepo}"`); | ||
|
|
||
| const sshConfig = `\nHost key-${sha256}.github.com\n` | ||
| + ` HostName github.com\n` | ||
| + ` IdentityFile ${homeSsh}/key-${sha256}\n` | ||
| + ` IdentityFile ${filename}\n` | ||
| + ` IdentitiesOnly yes\n`; | ||
|
|
||
| fs.appendFileSync(`${homeSsh}/config`, sshConfig); | ||
| fs.appendFileSync(`${homeSsh}/config`, sshConfig); | ||
|
|
||
| console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); | ||
| console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); | ||
| }) | ||
| }); | ||
|
|
||
| console.log("Key(s) added:"); | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-l'], { stdio: 'inherit' }); | ||
| } catch (error) { | ||
|
|
||
| if (error.code == 'ENOENT') { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,18 +2,20 @@ const core = require('@actions/core'); | |
| const child_process = require('child_process'); | ||
| const fs = require('fs'); | ||
| const crypto = require('crypto'); | ||
| const { homePath, sshAgentCmd, sshAddCmd, gitCmd } = require('./paths.js'); | ||
| const { homePath, sshAgentCmd, sshAddCmd, sshKeyGenCmd, gitCmd } = require('./paths.js'); | ||
|
|
||
| try { | ||
| const privateKey = core.getInput('ssh-private-key'); | ||
| const privateKeys = core.getInput('ssh-private-keys'); | ||
| const logPublicKey = core.getBooleanInput('log-public-key', {default: true}); | ||
|
|
||
| if (!privateKey) { | ||
| core.setFailed("The ssh-private-key argument is empty. Maybe the secret has not been configured, or you are using a wrong secret name in your workflow file."); | ||
| if (!privateKeys) { | ||
| core.setFailed("The ssh-private-keys Array{name, key} argument is empty. Maybe the secret has not been configured, or you are using a wrong secret name in your workflow file."); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| const privateKeysData = JSON.parse(privateKeys.replaceAll("\n", "")) | ||
|
|
||
| const homeSsh = homePath + '/.ssh'; | ||
|
|
||
| console.log(`Adding GitHub.com keys to ${homeSsh}/known_hosts`); | ||
|
|
@@ -39,47 +41,58 @@ try { | |
| } | ||
| }); | ||
|
|
||
| console.log("Adding private key(s) to agent"); | ||
| console.log("Adding private key(s) to agent and Configuring deployment key(s)"); | ||
|
|
||
| privateKey.split(/(?=-----BEGIN)/).forEach(function(key) { | ||
| child_process.execFileSync(sshAddCmd, ['-'], { input: key.trim() + "\n" }); | ||
| }); | ||
| privateKeysData.forEach(async ({ name, key }) => { | ||
| const repoName = name.trim(); | ||
| let privateKey = key.trim(); | ||
|
|
||
| console.log("Key(s) added:"); | ||
| privateKey = privateKey.replace(/(KEY-----)(...)/, '$1\n$2') | ||
| .replace(/(...)(-----END )/, '$1\n$2') + "\n" | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-l'], { stdio: 'inherit' }); | ||
| child_process.execFileSync(sshAddCmd, ['-'], { input: privateKey }); | ||
|
|
||
| console.log('Configuring deployment key(s)'); | ||
| const sha256 = crypto.createHash('sha256').update(privateKey).digest('hex'); | ||
| const filename = `${homeSsh}/key-${sha256}` | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-L']).toString().trim().split(/\r?\n/).forEach(function(key) { | ||
| const parts = key.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); | ||
|
|
||
| if (!parts) { | ||
| if (logPublicKey) { | ||
| console.log(`Comment for (public) key '${key}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); | ||
| await fs.writeFile(filename, privateKey, { }, (err) => { | ||
| if (err) { | ||
| console.log(err) | ||
| return | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| const sha256 = crypto.createHash('sha256').update(key).digest('hex'); | ||
| const ownerAndRepo = parts[1].replace(/\.git$/, ''); | ||
| fs.chmodSync(filename, '600') | ||
|
|
||
| fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' }); | ||
| const parts = repoName.match(/\bgithub\.com[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)/i); | ||
|
|
||
| child_process.execSync(`${gitCmd} config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "[email protected]:${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://[email protected]/${ownerAndRepo}"`); | ||
| if (!parts) { | ||
| if (logPublicKey) { | ||
| console.log(`Comment for name '${repoName}' does not match GitHub URL pattern. Not treating it as a GitHub deploy key.`); | ||
| } | ||
|
|
||
| const sshConfig = `\nHost key-${sha256}.github.com\n` | ||
| return; | ||
| } | ||
|
|
||
| const ownerAndRepo = parts[1].replace(/\.git$/, ''); | ||
|
|
||
| child_process.execSync(`${gitCmd} config --global --replace-all url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "https://github.com/${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "[email protected]:${ownerAndRepo}"`); | ||
| child_process.execSync(`${gitCmd} config --global --add url."git@key-${sha256}.github.com:${ownerAndRepo}".insteadOf "ssh://[email protected]/${ownerAndRepo}"`); | ||
|
|
||
| const sshConfig = `\nHost key-${sha256}.github.com\n` | ||
| + ` HostName github.com\n` | ||
| + ` IdentityFile ${homeSsh}/key-${sha256}\n` | ||
| + ` IdentityFile ${filename}\n` | ||
| + ` IdentitiesOnly yes\n`; | ||
|
|
||
| fs.appendFileSync(`${homeSsh}/config`, sshConfig); | ||
| fs.appendFileSync(`${homeSsh}/config`, sshConfig); | ||
|
|
||
| console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); | ||
| console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`); | ||
| }) | ||
| }); | ||
|
|
||
| console.log("Key(s) added:"); | ||
|
|
||
| child_process.execFileSync(sshAddCmd, ['-l'], { stdio: 'inherit' }); | ||
| } catch (error) { | ||
|
|
||
| if (error.code == 'ENOENT') { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for, given the next step is "Setup node"? Does that next step not work locally and becomes a "no-op" if nodejs is already installed and at the right version?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is "hack" for local container when using with
acttool ( only executed whenif: ${{ env.ACT }}) - got same error as others with node missing. Some "known" issue with the tool when using following combo:This is error without this:

via nektos/act#917 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha... so the action to setup node uses node to run :) - how nice.
But yeah, I noticed in an open source project i contributed to that Github's image "ubuntu-latest" is actually different compared to the "ubuntu:latest" and has more things installed already.
Btw, why do you use (overwrite) "ubuntu:latest" as the image and not let
actpick the right image for "ubuntu-latest"?acthas an imageghcr.io/catthehacker/ubuntu:act-latest- maybe that one containsnodealready like github's runner image?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems their image does install node: https://github.com/catthehacker/docker_images/blob/master/linux/ubuntu/scripts/act.sh#L128
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was taken from parent fork, to test as container. will see if I have possibility to dig more next week