Skip to content

Support Forgejo (& GHES?) #236

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ The following inputs can be used to control the action's behavior:

* `ssh-private-key`: Required. Use this to provide the key(s) to load as GitHub Actions secrets.
* `ssh-auth-sock`: Can be used to control where the SSH agent socket will be placed. Ultimately affects the `$SSH_AUTH_SOCK` environment variable.
* `instance-urls`: Can be used to determine what Git provider URLs to check keys against. Defaults to `github.com`.
* `log-public-key`: Set this to `false` if you want to suppress logging of _public_ key information. To simplify debugging and since it contains public key information only, this is turned on by default.
* `ssh-agent-cmd`: Optional. Use this to specify a custom location for the `ssh-agent` binary.
* `ssh-add-cmd`: Optional. Use this to specify a custom location for the `ssh-add` binary.
Expand Down
12 changes: 12 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ inputs:
required: true
ssh-auth-sock:
description: 'Where to place the SSH Agent auth socket'
instance-urls:
description: |-
URL(s) of the Git provider instance(s) to use.
You can specify multiple instance URls by putting each one on a separate line.
```yaml
instance-urls: |-
github.com
code.forgejo.org
codeberg.org
```
required: false
default: 'github.com'
log-public-key:
description: 'Whether or not to log public key fingerprints'
required: false
Expand Down
48 changes: 29 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { homePath, sshAgentCmd, sshAddCmd, gitCmd } = require('./paths.js');
try {
const privateKey = core.getInput('ssh-private-key');
const logPublicKey = core.getBooleanInput('log-public-key', {default: true});
const instanceUrls = core.getInput('instance-urls', { required: 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.");
Expand Down Expand Up @@ -45,33 +46,42 @@ try {

console.log('Configuring deployment key(s)');

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 instanceUrlsArray = instanceUrls.split(/\r?\n/);

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.`);
instanceUrlsArray.forEach(instanceUrl => {
const urlPattern = new RegExp(`\\b${instanceUrl.replace(/\./g, '\\.')}[:/]([_.a-z0-9-]+\/[_.a-z0-9-]+)`, 'i');

child_process.execFileSync(sshAddCmd, ['-L']).toString().trim().split(/\r?\n/).forEach(function(key) {
const parts = key.match(urlPattern);

if (!parts) {
if (logPublicKey) {
console.log(`Comment for (public) key '${key}' does not match ${instanceUrl} URL pattern. Not treating it as a deploy key for ${instanceUrl}.`);
}
return;
}
return;
}

const sha256 = crypto.createHash('sha256').update(key).digest('hex');
const ownerAndRepo = parts[1].replace(/\.git$/, '');
const sha256 = crypto.createHash('sha256').update(key).digest('hex');
const ownerAndRepo = parts[1].replace(/\.git$/, '');

fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' });

const keyHostname = `key-${sha256}.${instanceUrl}`;

fs.writeFileSync(`${homeSsh}/key-${sha256}`, key + "\n", { mode: '600' });
child_process.execSync(`${gitCmd} config --global --replace-all url."git@${keyHostname}:${ownerAndRepo}".insteadOf "https://${instanceUrl}/${ownerAndRepo}"`);
child_process.execSync(`${gitCmd} config --global --add url."git@${keyHostname}:${ownerAndRepo}".insteadOf "git@${instanceUrl}:${ownerAndRepo}"`);
child_process.execSync(`${gitCmd} config --global --add url."git@${keyHostname}:${ownerAndRepo}".insteadOf "ssh://git@${instanceUrl}/${ownerAndRepo}"`);

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 ${keyHostname}\n`
+ ` HostName ${instanceUrl}\n`
+ ` IdentityFile ${homeSsh}/key-${sha256}\n`
+ ` IdentitiesOnly yes\n`;

const sshConfig = `\nHost key-${sha256}.github.com\n`
+ ` HostName github.com\n`
+ ` IdentityFile ${homeSsh}/key-${sha256}\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 ${instanceUrl} repository ${ownerAndRepo}`);

console.log(`Added deploy-key mapping: Use identity '${homeSsh}/key-${sha256}' for GitHub repository ${ownerAndRepo}`);
});
});

} catch (error) {
Expand Down