diff --git a/README.md b/README.md index 050c90a..bb6fd7b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ If this value is set to the name of a valid environment in the target repositori ### `target` -Target where secrets should be stored: `actions` (default) or `dependabot`. +Target where secrets should be stored: `actions` (default), `codespaces` or `dependabot`. ## Usage diff --git a/__tests__/github.test.ts b/__tests__/github.test.ts index 9b78cc1..44dee31 100644 --- a/__tests__/github.test.ts +++ b/__tests__/github.test.ts @@ -135,8 +135,10 @@ describe("setSecretForRepo", () => { let actionsPublicKeyMock: nock.Scope; let dependabotPublicKeyMock: nock.Scope; + let codespacesPublicKeyMock: nock.Scope; let setActionsSecretMock: nock.Scope; let setDependabotSecretMock: nock.Scope; + let setCodespacesSecretMock: nock.Scope; beforeEach(() => { nock.cleanAll(); @@ -151,6 +153,10 @@ describe("setSecretForRepo", () => { .get(`/repos/${repo.full_name}/dependabot/secrets/public-key`) .reply(200, publicKey); + codespacesPublicKeyMock = nock("https://api.github.com") + .get(`/repos/${repo.full_name}/codespaces/secrets/public-key`) + .reply(200, publicKey); + setActionsSecretMock = nock("https://api.github.com") .put(`/repos/${repo.full_name}/actions/secrets/FOO`, (body) => { expect(body.encrypted_value).toBeTruthy(); @@ -166,6 +172,14 @@ describe("setSecretForRepo", () => { return body; }) .reply(200); + + setCodespacesSecretMock = nock("https://api.github.com") + .put(`/repos/${repo.full_name}/codespaces/secrets/FOO`, (body) => { + expect(body.encrypted_value).toBeTruthy(); + expect(body.key_id).toEqual(publicKey.key_id); + return body; + }) + .reply(200); }); test("setSecretForRepo with Actions target should retrieve public key for Actions", async () => { @@ -194,6 +208,19 @@ describe("setSecretForRepo", () => { expect(dependabotPublicKeyMock.isDone()).toBeTruthy(); }); + test("setSecretForRepo with Codespaces target should retrieve public key for Codespaces", async () => { + await setSecretForRepo( + octokit, + "FOO", + secrets.FOO, + repo, + "", + true, + "codespaces" + ); + expect(codespacesPublicKeyMock.isDone()).toBeTruthy(); + }); + test("setSecretForRepo should not set secret with dry run", async () => { await setSecretForRepo( octokit, @@ -233,6 +260,19 @@ describe("setSecretForRepo", () => { ); expect(setDependabotSecretMock.isDone()).toBeTruthy(); }); + + test("setSecretForRepo with Codespaces target should call set secret endpoint for Codespaces", async () => { + await setSecretForRepo( + octokit, + "FOO", + secrets.FOO, + repo, + "", + false, + "codespaces" + ); + expect(setCodespacesSecretMock.isDone()).toBeTruthy(); + }); }); describe("setSecretForRepo with environment", () => { @@ -336,6 +376,7 @@ describe("deleteSecretForRepo", () => { const secrets = { FOO: "BAR" }; let deleteActionsSecretMock: nock.Scope; let deleteDependabotSecretMock: nock.Scope; + let deleteCodespacesSecretMock: nock.Scope; beforeEach(() => { nock.cleanAll(); @@ -347,6 +388,10 @@ describe("deleteSecretForRepo", () => { deleteDependabotSecretMock = nock("https://api.github.com") .delete(`/repos/${repo.full_name}/dependabot/secrets/FOO`) .reply(200); + + deleteCodespacesSecretMock = nock("https://api.github.com") + .delete(`/repos/${repo.full_name}/codespaces/secrets/FOO`) + .reply(200); }); test("deleteSecretForRepo should not delete secret with dry run", async () => { @@ -387,6 +432,19 @@ describe("deleteSecretForRepo", () => { ); expect(deleteDependabotSecretMock.isDone()).toBeTruthy(); }); + + test("deleteSecretForRepo with Codespaces target should call set secret endpoint for Codespaces", async () => { + await deleteSecretForRepo( + octokit, + "FOO", + secrets.FOO, + repo, + "", + false, + "codespaces" + ); + expect(deleteCodespacesSecretMock.isDone()).toBeTruthy(); + }); }); describe("deleteSecretForRepo with environment", () => { diff --git a/action.yml b/action.yml index e435868..c16a312 100644 --- a/action.yml +++ b/action.yml @@ -64,7 +64,7 @@ inputs: required: false target: description: | - Target where secrets should be stored: `actions` (default) or `dependabot`. + Target where secrets should be stored: `actions` (default), `codespaces` or `dependabot`. default: "actions" required: false runs: diff --git a/dist/index.js b/dist/index.js index 238fa8f..fe3466d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -236,6 +236,13 @@ function getPublicKey(octokit, repo, environment, target) { else { const [owner, name] = repo.full_name.split("/"); switch (target) { + case "codespaces": + publicKey = (yield octokit.codespaces.getRepoPublicKey({ + owner, + repo: name, + })).data; + exports.publicKeyCache.set(repo, publicKey); + return publicKey; case "dependabot": publicKey = (yield octokit.dependabot.getRepoPublicKey({ owner, @@ -266,6 +273,14 @@ function setSecretForRepo(octokit, name, secret, repo, environment, dry_run, tar core.info(`Set \`${name} = ***\` on ${repo.full_name}`); if (!dry_run) { switch (target) { + case "codespaces": + return octokit.codespaces.createOrUpdateRepoSecret({ + owner: repo_owner, + repo: repo_name, + secret_name: name, + key_id: publicKey.key_id, + encrypted_value, + }); case "dependabot": return octokit.dependabot.createOrUpdateRepoSecret({ owner: repo_owner, @@ -306,6 +321,8 @@ function deleteSecretForRepo(octokit, name, secret, repo, environment, dry_run, if (!dry_run) { const action = "DELETE"; switch (target) { + case "codespaces": + return octokit.request(`${action} /repos/${repo.full_name}/codespaces/secrets/${name}`); case "dependabot": return octokit.request(`${action} /repos/${repo.full_name}/dependabot/secrets/${name}`); case "actions": @@ -397,7 +414,7 @@ function run() { core.setFailed(`Secrets: no matches with "${config.SECRETS.join(", ")}"`); return; } - const allowedTargets = ["dependabot", "actions"]; + const allowedTargets = ["dependabot", "actions", "codespaces"]; if (!allowedTargets.some((x) => x === config.TARGET)) { core.setFailed(`Target: Value not in supported targets: ${allowedTargets}`); return; diff --git a/src/github.ts b/src/github.ts index 4fad064..11bf991 100644 --- a/src/github.ts +++ b/src/github.ts @@ -184,6 +184,17 @@ export async function getPublicKey( const [owner, name] = repo.full_name.split("/"); switch (target) { + case "codespaces": + publicKey = ( + await octokit.codespaces.getRepoPublicKey({ + owner, + repo: name, + }) + ).data as PublicKey; + + publicKeyCache.set(repo, publicKey); + + return publicKey; case "dependabot": publicKey = ( await octokit.dependabot.getRepoPublicKey({ @@ -232,6 +243,14 @@ export async function setSecretForRepo( if (!dry_run) { switch (target) { + case "codespaces": + return octokit.codespaces.createOrUpdateRepoSecret({ + owner: repo_owner, + repo: repo_name, + secret_name: name, + key_id: publicKey.key_id, + encrypted_value, + }); case "dependabot": return octokit.dependabot.createOrUpdateRepoSecret({ owner: repo_owner, @@ -278,6 +297,10 @@ export async function deleteSecretForRepo( if (!dry_run) { const action = "DELETE"; switch (target) { + case "codespaces": + return octokit.request( + `${action} /repos/${repo.full_name}/codespaces/secrets/${name}` + ); case "dependabot": return octokit.request( `${action} /repos/${repo.full_name}/dependabot/secrets/${name}` diff --git a/src/main.ts b/src/main.ts index 04cd41b..ba4dcea 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,7 +40,7 @@ export async function run(): Promise { return; } - const allowedTargets = ["dependabot", "actions"]; + const allowedTargets = ["dependabot", "actions", "codespaces"]; if (!allowedTargets.some((x) => x === config.TARGET)) { core.setFailed( `Target: Value not in supported targets: ${allowedTargets}`