From aa14bde01fc760a3410b75ecf95c090ba4734f93 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Thu, 25 Sep 2025 16:00:57 +0200 Subject: [PATCH] Support cloning via SSH Enables deploying from private repositories. Fixes #1 --- README.md | 11 +++++++++++ action.yml | 4 ++++ dist/index.js | 27 +++++++++++++++++++++------ src/main.ts | 30 ++++++++++++++++++++++++------ 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 03e1e67..285e02d 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,17 @@ There are many specifics that you have to adhere to in the Compose file used tha These specifics are related to how traffic gets routed to your services and how to isolate your services from other deployments on the same server. This is explained in the [Compose file section](#compose-file-1) below. +### clone-using +How to clone the repository. Valid values are `ssh` and `https`. +Defaults to `https`. + +If using `ssh` you need to make sure that the following +public SSH key has read access to the repository + +`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHVy+36j5AUdxkifVpA/akT3Y1ZkZPnEpaLaZWPRpNlW root@branch` + +SSH cloning is required for private repositories. + ## Outputs ### url The complete URL where the system can be accessed. diff --git a/action.yml b/action.yml index c18bbdd..58bfc90 100644 --- a/action.yml +++ b/action.yml @@ -17,6 +17,10 @@ inputs: description: 'The Docker Compose file to use' default: 'compose.yaml' required: true + clone-using: + description: 'The method to use for cloning the repository, either ssh or https' + default: 'https' + required: true outputs: url: description: 'The URL of the deployed branch' diff --git a/dist/index.js b/dist/index.js index ba40f6b..e1d08ab 100644 --- a/dist/index.js +++ b/dist/index.js @@ -52,24 +52,39 @@ function getSSHKey() { return core.getInput('ssh-key', { required: true }); } } +function getCloneUrl(serverUrl, owner, repositoryName) { + const cloneUsing = core.getInput('clone-using', { required: true }); + if (cloneUsing === 'https') { + return `${serverUrl}/${owner}/${repositoryName}.git`; + } + else if (cloneUsing === 'ssh') { + const url = new URL(serverUrl); + return `git@${url.hostname}:${owner}/${repositoryName}.git`; + } + else { + throw new Error("Invalid clone-using option, use 'ssh' or 'https'"); + } +} function main() { return __awaiter(this, void 0, void 0, function* () { - const branchName = process.env.GITHUB_HEAD_REF; + const branchName = process.env.GITHUB_HEAD_REF || 'main'; const repositoryName = gitea.context.repo.repo; const owner = gitea.context.repo.owner; const serverUrl = gitea.context.serverUrl; const mangledBranchName = branchName.replace(/\//g, '-').replace(/[^a-zA-Z0-9-]/g, ''); const composeFile = core.getInput('compose-file', { required: true }); + const cloneUrl = getCloneUrl(serverUrl, owner, repositoryName); core.info(`Branch name: ${branchName}`); core.info(`Repository name: ${repositoryName}`); + core.info(`Cloning from: ${cloneUrl}`); core.setOutput('url', `https://${repositoryName}-${mangledBranchName}.branch.dsv.su.se`); const sshKey = getSSHKey(); - core.info(`sshKey: ${sshKey.length}`); fs.writeFileSync('ssh_key', `${sshKey}\n`, { flag: 'w+', mode: "0600" }); - const deployCommand = `\ - echo "${serverUrl}/${owner}/${repositoryName}.git ${branchName} ${mangledBranchName} ${composeFile}" | \ - ssh -i ssh_key -o StrictHostKeyChecking=accept-new branch.dsv.su.se`; - const deployment = yield exec.getExecOutput('sh', ['-c', deployCommand]); + const sshStdin = `${cloneUrl} ${branchName} ${mangledBranchName} ${repositoryName} ${composeFile}`; + core.info(`Deploy arguments: ${sshStdin}`); + const deployment = yield exec.getExecOutput('ssh', ['-i', 'ssh_key', '-o', 'StrictHostKeyChecking=accept-new', 'branch.dsv.su.se'], { + input: Buffer.from(sshStdin) + }); if (deployment.exitCode != 0) { core.error(`Deployment failed: ${deployment.stderr}`); } diff --git a/src/main.ts b/src/main.ts index 998b7a2..71486a7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,27 +12,45 @@ function getSSHKey() { } } +function getCloneUrl(serverUrl: string, owner: string, repositoryName: string) { + const cloneUsing = core.getInput('clone-using', {required: true}); + if (cloneUsing === 'https') { + return `${serverUrl}/${owner}/${repositoryName}.git`; + } else if (cloneUsing === 'ssh') { + const url = new URL(serverUrl); + return `git@${url.hostname}:${owner}/${repositoryName}.git`; + } else { + throw new Error("Invalid clone-using option, use 'ssh' or 'https'"); + } +} + async function main() { - const branchName = process.env.GITHUB_HEAD_REF!; + const branchName = process.env.GITHUB_HEAD_REF || 'main'; const repositoryName = gitea.context.repo.repo; const owner = gitea.context.repo.owner; const serverUrl = gitea.context.serverUrl; const mangledBranchName = branchName.replace(/\//g, '-').replace(/[^a-zA-Z0-9-]/g, ''); const composeFile = core.getInput('compose-file', {required: true}); + const cloneUrl = getCloneUrl(serverUrl, owner, repositoryName); core.info(`Branch name: ${branchName}`); core.info(`Repository name: ${repositoryName}`); + core.info(`Cloning from: ${cloneUrl}`); core.setOutput('url', `https://${repositoryName}-${mangledBranchName}.branch.dsv.su.se`); const sshKey = getSSHKey(); - core.info(`sshKey: ${sshKey.length}`); fs.writeFileSync('ssh_key', `${sshKey}\n`, {flag: 'w+', mode: "0600"}); - const deployCommand = `\ - echo "${serverUrl}/${owner}/${repositoryName}.git ${branchName} ${mangledBranchName} ${composeFile}" | \ - ssh -i ssh_key -o StrictHostKeyChecking=accept-new branch.dsv.su.se`; - const deployment = await exec.getExecOutput('sh', ['-c', deployCommand]) + const sshStdin = `${cloneUrl} ${branchName} ${mangledBranchName} ${repositoryName} ${composeFile}`; + core.info(`Deploy arguments: ${sshStdin}`); + const deployment = await exec.getExecOutput( + 'ssh', + ['-i', 'ssh_key', '-o', 'StrictHostKeyChecking=accept-new', 'branch.dsv.su.se'], + { + input: Buffer.from(sshStdin) + } + ); if (deployment.exitCode != 0) { core.error(`Deployment failed: ${deployment.stderr}`); } else { -- 2.39.5