diff --git a/README.md b/README.md index e1318f1..fdc972d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,13 @@ A Github Action that can sync secrets from one repository to many others. This a ### `repositories` -**Required** New line deliminated regex expressions to select repositories. Repositires are limited to those in whcich the token user is an owner or collaborator. +**Required** New line deliminated regex expressions to select repositories. Repositires are limited to those in whcich the token user is an owner or collaborator. Set `repositories_list_regex` to `False` to use a hardcoded list of repositories. + +### `repositories_list_regex` +If this value is `true` (default), the action will find all +repositories available to the token user and filter based upon the regex +provided. If it is false, it is expected that `repositories` will be an a +new line deliminated list in the form of org/name. ### `secrets` diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index 774c72e..7b7e25c 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -27,6 +27,7 @@ function clearInputs() { describe("getConfig", () => { const SECRETS = ["FOO.*", "^BAR$"]; const REPOSITORIES = ["google/baz.*", "^google/foo$"]; + const REPOSITORIES_LIST_REGEX = true; const GITHUB_TOKEN = "token"; const DRY_RUN = false; @@ -34,6 +35,7 @@ describe("getConfig", () => { INPUT_GITHUB_TOKEN: GITHUB_TOKEN, INPUT_SECRETS: SECRETS.join("\n"), INPUT_REPOSITORIES: REPOSITORIES.join("\n"), + INPUT_REPOSITORIES_LIST_REGEX: String(REPOSITORIES_LIST_REGEX), INPUT_DRY_RUN: String(DRY_RUN) }; @@ -56,6 +58,7 @@ describe("getConfig", () => { GITHUB_TOKEN, SECRETS, REPOSITORIES, + REPOSITORIES_LIST_REGEX, DRY_RUN }); }); diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 2223146..5123e68 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -44,8 +44,40 @@ test("run should succeed with a repo and secret", async () => { GITHUB_TOKEN: "token", SECRETS: ["BAZ"], REPOSITORIES: [".*"], + REPOSITORIES_LIST_REGEX: true, DRY_RUN: false }); await run(); + + expect(github.listAllMatchingRepos as jest.Mock).toBeCalledTimes(1); + expect((github.setSecretsForRepo as jest.Mock).mock.calls[0][2]).toEqual( + fixture[0].response + ); + + expect(process.exitCode).toBe(undefined); +}); + +test("run should succeed with a repo and secret with repository_list_regex as false", async () => { + (github.setSecretsForRepo 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 + }); + await run(); + + expect((github.setSecretsForRepo as jest.Mock).mock.calls[0][2]).toEqual({ + full_name: fixture[0].response.full_name + }); + expect(process.exitCode).toBe(undefined); }); diff --git a/action.yml b/action.yml index 1cc70d2..c3e6036 100644 --- a/action.yml +++ b/action.yml @@ -11,10 +11,19 @@ inputs: required: true repositories: description: | - New line deliminated regex expressions to select repositories. - Repositires are limited to those in whcich the token user is an owner - or collaborator. + New line deliminated regex expressions to select repositories. Repositires + are limited to those in which the token user is an owner or collaborator. + Set `REPOSITORIES_LIST_REGEX` to `False` to use a hardcoded list of + repositories. required: true + repositories_list_regex: + default: "true" + description: | + If this value is `true`(default), the action will find all repositories + available to the token user and filter based upon the regex provided. If + it is false, it is expected that `REPOSITORIES` will be an a new line + deliminated list in the form of org/name. + required: false secrets: description: | New line deliminated regex expressions to select values from `process.env`. diff --git a/dist/index.js b/dist/index.js index efc3781..3c02162 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2119,20 +2119,30 @@ function run() { const octokit = github_1.DefaultOctokit({ auth: config.GITHUB_TOKEN }); - const repos = yield github_1.listAllMatchingRepos({ - patterns: config.REPOSITORIES, - octokit - }); + let repos; + if (config.REPOSITORIES_LIST_REGEX) { + repos = yield github_1.listAllMatchingRepos({ + patterns: config.REPOSITORIES, + octokit + }); + } + else { + repos = config.REPOSITORIES.map(s => { + return { + full_name: s + }; + }); + } /* istanbul ignore next */ if (repos.length === 0) { const repoPatternString = config.REPOSITORIES.join(", "); core.setFailed(`Repos: No matches with "${repoPatternString}". Check your token and regex.`); return; } - // eslint-disable-next-line @typescript-eslint/promise-function-async const repoNames = repos.map(r => r.full_name); core.info(JSON.stringify({ REPOSITORIES: config.REPOSITORIES, + REPOSITORIES_LIST_REGEX: config.REPOSITORIES_LIST_REGEX, SECRETS: config.SECRETS, DRY_RUN: config.DRY_RUN, FOUND_REPOS: repoNames, @@ -5495,6 +5505,9 @@ function getConfig() { GITHUB_TOKEN: core.getInput("GITHUB_TOKEN", { required: true }), SECRETS: core.getInput("SECRETS", { required: true }).split("\n"), REPOSITORIES: core.getInput("REPOSITORIES", { required: true }).split("\n"), + REPOSITORIES_LIST_REGEX: ["1", "true"].includes(core + .getInput("REPOSITORIES_LIST_REGEX", { required: false }) + .toLowerCase()), DRY_RUN: ["1", "true"].includes(core.getInput("DRY_RUN", { required: false }).toLowerCase()) }; if (config.DRY_RUN) { @@ -7428,7 +7441,6 @@ const defaultOptions = { onAbuseLimit } }; -// eslint-disable-next-line @typescript-eslint/promise-function-async function DefaultOctokit(_a) { var options = __rest(_a, []); return new RetryOctokit(Object.assign(Object.assign({}, defaultOptions), options)); diff --git a/src/config.ts b/src/config.ts index 6648359..18ca891 100644 --- a/src/config.ts +++ b/src/config.ts @@ -20,6 +20,7 @@ export interface Config { GITHUB_TOKEN: string; SECRETS: string[]; REPOSITORIES: string[]; + REPOSITORIES_LIST_REGEX: boolean; DRY_RUN: boolean; } @@ -28,6 +29,11 @@ export function getConfig(): Config { GITHUB_TOKEN: core.getInput("GITHUB_TOKEN", { required: true }), SECRETS: core.getInput("SECRETS", { required: true }).split("\n"), REPOSITORIES: core.getInput("REPOSITORIES", { required: true }).split("\n"), + REPOSITORIES_LIST_REGEX: ["1", "true"].includes( + core + .getInput("REPOSITORIES_LIST_REGEX", { required: false }) + .toLowerCase() + ), DRY_RUN: ["1", "true"].includes( core.getInput("DRY_RUN", { required: false }).toLowerCase() ) diff --git a/src/github.ts b/src/github.ts index 3750b6d..1326455 100644 --- a/src/github.ts +++ b/src/github.ts @@ -20,6 +20,10 @@ import { Octokit } from "@octokit/rest"; import { encrypt } from "./utils"; import { retry } from "@octokit/plugin-retry"; +export interface Repository { + full_name: string; +} + const RetryOctokit = Octokit.plugin(retry); /* istanbul ignore next */ @@ -47,7 +51,6 @@ const defaultOptions = { } }; -// eslint-disable-next-line @typescript-eslint/promise-function-async export function DefaultOctokit({ ...options }): any { return new RetryOctokit({ ...defaultOptions, ...options }); } @@ -62,7 +65,7 @@ export async function listAllMatchingRepos({ octokit: any; affiliation?: string; pageSize?: number; -}): Promise { +}): Promise { const repos = await listAllReposForAuthenticatedUser({ octokit, affiliation, @@ -84,8 +87,8 @@ export async function listAllReposForAuthenticatedUser({ octokit: any; affiliation: string; pageSize: number; -}): Promise { - const repos: any[] = []; +}): Promise { + const repos: Repository[] = []; for (let page = 1; ; page++) { const response = await octokit.repos.listForAuthenticatedUser({ @@ -102,7 +105,10 @@ export async function listAllReposForAuthenticatedUser({ return repos; } -export function filterReposByPatterns(repos: any[], patterns: string[]): any[] { +export function filterReposByPatterns( + repos: Repository[], + patterns: string[] +): Repository[] { const regexPatterns = patterns.map(s => new RegExp(s)); return repos.filter( @@ -113,7 +119,7 @@ export function filterReposByPatterns(repos: any[], patterns: string[]): any[] { export async function setSecretsForRepo( octokit: any, secrets: { [key: string]: string }, - repo: any, + repo: Repository, dry_run: boolean ): Promise { const [owner, name] = repo.full_name.split("/"); diff --git a/src/main.ts b/src/main.ts index 5d479ee..713637d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,6 +18,7 @@ import * as core from "@actions/core"; import { DefaultOctokit, + Repository, listAllMatchingRepos, setSecretsForRepo } from "./github"; @@ -40,10 +41,19 @@ export async function run(): Promise { auth: config.GITHUB_TOKEN }); - const repos = await listAllMatchingRepos({ - patterns: config.REPOSITORIES, - octokit - }); + let repos: Repository[]; + if (config.REPOSITORIES_LIST_REGEX) { + repos = await listAllMatchingRepos({ + patterns: config.REPOSITORIES, + octokit + }); + } else { + repos = config.REPOSITORIES.map(s => { + return { + full_name: s + }; + }); + } /* istanbul ignore next */ if (repos.length === 0) { @@ -54,13 +64,13 @@ export async function run(): Promise { return; } - // eslint-disable-next-line @typescript-eslint/promise-function-async const repoNames = repos.map(r => r.full_name); core.info( JSON.stringify( { REPOSITORIES: config.REPOSITORIES, + REPOSITORIES_LIST_REGEX: config.REPOSITORIES_LIST_REGEX, SECRETS: config.SECRETS, DRY_RUN: config.DRY_RUN, FOUND_REPOS: repoNames,