Skip to content

Commit

Permalink
feat: add target parameter for Dependabot secrets (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
Laugslander authored Dec 7, 2023
1 parent 6758554 commit b2d20d6
Show file tree
Hide file tree
Showing 11 changed files with 720 additions and 685 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ When set to `true`, the action will find and delete the selected secrets from re

### `environment`

If this value is set to the name of a valid environment in the target repositories, the action will not set repository secrets but instead only set environment secrets for the specified environment. When not set, will set repository secrets only.
If this value is set to the name of a valid environment in the target repositories, the action will not set repository secrets but instead only set environment secrets for the specified environment. When not set, will set repository secrets only. Only works if `target` is set to `actions` (default).

### `target`

Target where secrets should be stored: `actions` (default) or `dependabot`.

## Usage

Expand Down
4 changes: 4 additions & 0 deletions __tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe("getConfig", () => {
const CONCURRENCY = 50;
const RUN_DELETE = false;
const ENVIRONMENT = "production";
const TARGET = "actions";

// Must implement because operands for delete must be optional in typescript >= 4.0
interface Inputs {
Expand All @@ -49,6 +50,7 @@ describe("getConfig", () => {
INPUT_CONCURRENCY: string;
INPUT_RUN_DELETE: string;
INPUT_ENVIRONMENT: string;
INPUT_TARGET: string;
}
const inputs: Inputs = {
INPUT_GITHUB_API_URL: String(GITHUB_API_URL),
Expand All @@ -61,6 +63,7 @@ describe("getConfig", () => {
INPUT_CONCURRENCY: String(CONCURRENCY),
INPUT_RUN_DELETE: String(RUN_DELETE),
INPUT_ENVIRONMENT: String(ENVIRONMENT),
INPUT_TARGET: String(TARGET),
};

beforeEach(() => {
Expand Down Expand Up @@ -89,6 +92,7 @@ describe("getConfig", () => {
CONCURRENCY,
RUN_DELETE,
ENVIRONMENT,
TARGET,
});
});

Expand Down
185 changes: 159 additions & 26 deletions __tests__/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,39 +133,105 @@ describe("setSecretForRepo", () => {

const repoEnvironment = "production";

let publicKeyMock: nock.Scope;
let setSecretMock: nock.Scope;
let actionsPublicKeyMock: nock.Scope;
let dependabotPublicKeyMock: nock.Scope;
let setActionsSecretMock: nock.Scope;
let setDependabotSecretMock: nock.Scope;

beforeEach(() => {
nock.cleanAll();

publicKeyCache.clear();
publicKeyMock = nock("https://api.github.com")

actionsPublicKeyMock = nock("https://api.github.com")
.get(`/repos/${repo.full_name}/actions/secrets/public-key`)
.reply(200, publicKey);

setSecretMock = nock("https://api.github.com")
dependabotPublicKeyMock = nock("https://api.github.com")
.get(`/repos/${repo.full_name}/dependabot/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();
expect(body.key_id).toEqual(publicKey.key_id);
return body;
})
.reply(200);

setDependabotSecretMock = nock("https://api.github.com")
.put(`/repos/${repo.full_name}/dependabot/secrets/FOO`, (body) => {
expect(body.encrypted_value).toBeTruthy();
expect(body.key_id).toEqual(publicKey.key_id);
return body;
})
.reply(200);
});

test("setSecretForRepo should retrieve public key", async () => {
await setSecretForRepo(octokit, "FOO", secrets.FOO, repo, "", true);
expect(publicKeyMock.isDone()).toBeTruthy();
test("setSecretForRepo with Actions target should retrieve public key for Actions", async () => {
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
true,
"actions"
);
expect(actionsPublicKeyMock.isDone()).toBeTruthy();
});

test("setSecretForRepo with Dependabot target should retrieve public key for Dependabot", async () => {
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
true,
"dependabot"
);
expect(dependabotPublicKeyMock.isDone()).toBeTruthy();
});

test("setSecretForRepo should not set secret with dry run", async () => {
await setSecretForRepo(octokit, "FOO", secrets.FOO, repo, "", true);
expect(publicKeyMock.isDone()).toBeTruthy();
expect(setSecretMock.isDone()).toBeFalsy();
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
true,
"actions"
);
expect(actionsPublicKeyMock.isDone()).toBeTruthy();
expect(setActionsSecretMock.isDone()).toBeFalsy();
});

test("setSecretForRepo should call set secret endpoint", async () => {
await setSecretForRepo(octokit, "FOO", secrets.FOO, repo, "", false);
expect(nock.isDone()).toBeTruthy();
test("setSecretForRepo with Actions target should call set secret endpoint for Actions", async () => {
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
false,
"actions"
);
expect(setActionsSecretMock.isDone()).toBeTruthy();
});

test("setSecretForRepo with Dependabot target should call set secret endpoint for Dependabot", async () => {
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
false,
"dependabot"
);
expect(setDependabotSecretMock.isDone()).toBeTruthy();
});
});

Expand Down Expand Up @@ -214,7 +280,8 @@ describe("setSecretForRepo with environment", () => {
secrets.FOO,
repo,
repoEnvironment,
true
true,
"actions"
);
expect(environmentPublicKeyMock.isDone()).toBeTruthy();
});
Expand All @@ -226,7 +293,22 @@ describe("setSecretForRepo with environment", () => {
secrets.FOO,
repo,
repoEnvironment,
true
true,
"actions"
);
expect(environmentPublicKeyMock.isDone()).toBeTruthy();
expect(setEnvironmentSecretMock.isDone()).toBeFalsy();
});

test("setSecretForRepo should not set secret with Dependabot target", async () => {
await setSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
repoEnvironment,
true,
"dependabot"
);
expect(environmentPublicKeyMock.isDone()).toBeTruthy();
expect(setEnvironmentSecretMock.isDone()).toBeFalsy();
Expand All @@ -239,7 +321,8 @@ describe("setSecretForRepo with environment", () => {
secrets.FOO,
repo,
repoEnvironment,
false
false,
"actions"
);
expect(nock.isDone()).toBeTruthy();
});
Expand All @@ -251,23 +334,58 @@ describe("deleteSecretForRepo", () => {
jest.setTimeout(30000);

const secrets = { FOO: "BAR" };
let deleteSecretMock: nock.Scope;
let deleteActionsSecretMock: nock.Scope;
let deleteDependabotSecretMock: nock.Scope;

beforeEach(() => {
nock.cleanAll();
deleteSecretMock = nock("https://api.github.com")

deleteActionsSecretMock = nock("https://api.github.com")
.delete(`/repos/${repo.full_name}/actions/secrets/FOO`)
.reply(200);

deleteDependabotSecretMock = nock("https://api.github.com")
.delete(`/repos/${repo.full_name}/dependabot/secrets/FOO`)
.reply(200);
});

test("deleteSecretForRepo should not delete secret with dry run", async () => {
await deleteSecretForRepo(octokit, "FOO", secrets.FOO, repo, "", true);
expect(deleteSecretMock.isDone()).toBeFalsy();
await deleteSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
true,
"actions"
);
expect(deleteActionsSecretMock.isDone()).toBeFalsy();
});

test("deleteSecretForRepo should call set secret endpoint", async () => {
await deleteSecretForRepo(octokit, "FOO", secrets.FOO, repo, "", false);
expect(nock.isDone()).toBeTruthy();
test("deleteSecretForRepo with Actions target should call set secret endpoint for Actions", async () => {
await deleteSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
false,
"actions"
);
expect(deleteActionsSecretMock.isDone()).toBeTruthy();
});

test("deleteSecretForRepo with Dependabot target should call set secret endpoint for Dependabot", async () => {
await deleteSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
"",
false,
"dependabot"
);
expect(deleteDependabotSecretMock.isDone()).toBeTruthy();
});
});

Expand Down Expand Up @@ -297,19 +415,34 @@ describe("deleteSecretForRepo with environment", () => {
secrets.FOO,
repo,
repoEnvironment,
true
true,
"actions"
);
expect(deleteSecretMock.isDone()).toBeFalsy();
});

test("deleteSecretForRepo should not delete secret with Dependabot target", async () => {
await deleteSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
repoEnvironment,
true,
"dependabot"
);
expect(deleteSecretMock.isDone()).toBeFalsy();
});

test("deleteSecretForRepo should call set secret endpoint", async () => {
test("deleteSecretForRepo with Actions target should call set secret endpoint for Actions", async () => {
await deleteSecretForRepo(
octokit,
"FOO",
secrets.FOO,
repo,
repoEnvironment,
false
false,
"actions"
);
expect(nock.isDone()).toBeTruthy();
});
Expand Down
30 changes: 30 additions & 0 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ test("run should succeed with a repo and secret", async () => {
DRY_RUN: false,
RETRIES: 3,
CONCURRENCY: 1,
TARGET: "actions",
});
await run();

Expand Down Expand Up @@ -79,6 +80,7 @@ test("run should succeed with a repo and secret with repository_list_regex as fa
REPOSITORIES_LIST_REGEX: false,
DRY_RUN: false,
CONCURRENCY: 1,
TARGET: "actions",
});
await run();

Expand All @@ -103,9 +105,37 @@ test("run should succeed with delete enabled, a repo and secret with repository_
DRY_RUN: false,
RUN_DELETE: true,
CONCURRENCY: 1,
TARGET: "actions",
});
await run();

expect(github.deleteSecretForRepo as jest.Mock).toBeCalledTimes(1);
expect(process.exitCode).toBe(undefined);
});

test("run should fail if target is not supported", async () => {
(github.getRepos as jest.Mock) = jest
.fn()
.mockImplementation(async () => [fixture[0].response]);

(github.setSecretForRepo as jest.Mock) = jest
.fn()
.mockImplementation(async () => null);

(secrets.getSecrets as jest.Mock) = jest.fn().mockReturnValue({
BAZ: "bar",
});

(config.getConfig as jest.Mock) = jest.fn().mockReturnValue({
GITHUB_TOKEN: "token",
SECRETS: ["BAZ"],
REPOSITORIES: [fixture[0].response.full_name],
REPOSITORIES_LIST_REGEX: false,
DRY_RUN: false,
CONCURRENCY: 1,
TARGET: "invalid",
});
await run();

expect(process.exitCode).toBe(1);
});
9 changes: 7 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ inputs:
environment:
default: ""
description: |
If this value is set, the action will set the secrets to the repositories
environment with the name of this value.
If this value is set, the action will set the secrets to the repositories environment with the name of this value.
Only works if `target` is set to `actions` (default).
required: false
target:
description: |
Target where secrets should be stored: `actions` (default) or `dependabot`.
default: "actions"
required: false
runs:
using: 'node16'
Expand Down
Loading

0 comments on commit b2d20d6

Please sign in to comment.