Skip to content

Commit

Permalink
feat: Add support for ignoring specific repositories (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanbuck authored Oct 24, 2022
1 parent 9401785 commit 49c6156
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 7 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Options:
ot set. [string]
-R, --octoherd-repos One or multiple repositories in the form of 'repo-owner/repo-n
ame'. 'repo-owner/*' will find all repositories for one owner.
'*' will find all repositories the user has access to. Will p
rompt for repositories if not set. [array]
'*' will find all repositories the user has access to.
To exclude a repository use '!repo-owner/repo'. Will prompt
for repositories if not set. [array]
--octoherd-cache Cache responses for debugging. Creates a ./cache folder if fla
g is set. Override by passing custom path [string]
--octoherd-debug Show debug logs [boolean] [default: false]
Expand All @@ -32,6 +33,8 @@ Examples:
octoherd/cli
octoherd run -S path/to/script.js -T $TOKEN -R Avoid any prompts
octoherd/cli --octoherd-bypass-confirms
octoherd run -S path/to/script.js -T $TOKEN -R Will fetch all repositories except repo-owner/hello-world
'repo-owner/*' -R '!repo-owner/hello-world
```

The `script` must export a `script` function which takes three parameters:
Expand Down
2 changes: 1 addition & 1 deletion bin/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const options = {
},
"octoherd-repos": {
description:
"One or multiple repositories in the form of 'repo-owner/repo-name'. 'repo-owner/*' will find all repositories for one owner. '*' will find all repositories the user has access to. Will prompt for repositories if not set.",
"One or multiple repositories in the form of 'repo-owner/repo-name'. 'repo-owner/*' will find all repositories for one owner. '*' will find all repositories the user has access to. To exclude a repository use '!repo-owner/repo'. Will prompt for repositories if not set.",
type: "string",
array: true,
alias: "R",
Expand Down
19 changes: 16 additions & 3 deletions lib/resolve-repositories.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
export async function resolveRepositories(state, repositories) {
const ignoreRepositories = repositories.reduce((memo, fullName) => {
if (fullName.startsWith('!')) {
memo.push(fullName.slice(1))
}
return memo;
}, []);

const repositoriesWithStars = repositories.filter((fullName) => {
return /^[a-z0-9_.-]+\/(\*|[a-z0-9_.-]+\*|\*[a-z0-9_.-]+|[a-z0-9_.-]+\*[a-z0-9_.-]+)$/i.test(
fullName
);
) && !fullName.startsWith('!');
});

const repositoriesWithoutStars = repositories.filter((fullName) => {
return !/\*/i.test(fullName);
return !/\*/i.test(fullName) && !fullName.startsWith('!');
});
const allRepositories = !!repositories.find((name) => name === "*");

Expand Down Expand Up @@ -79,6 +86,12 @@ export async function resolveRepositories(state, repositories) {

process.stdout.write("\n");

function includesRepository(list, repo) {
return list.some((x) => x.toLowerCase() === repo.full_name.toLowerCase())
}

// return array with unique repositories based by id (https://stackoverflow.com/a/56757215)
return resolvedRepositories.filter((repo, index, repoList) => repoList.findIndex(v2 => (v2.id === repo.id)) === index);
return resolvedRepositories
.filter((repo, index, repoList) => repoList.findIndex(v2 => (v2.id === repo.id)) === index)
.filter(repo => !includesRepository(ignoreRepositories, repo))
}
2 changes: 1 addition & 1 deletion lib/run-script-against-repositories.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function runScriptAgainstRepositories(state, octoherdRepos = []) {
const invalid = values.find((value) => {
if (value.trim() === "*") return;

if (/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.*-]+$/.test(value.trim())) {
if (/^!?[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.*-]+$/.test(value.trim())) {
return;
}

Expand Down
115 changes: 115 additions & 0 deletions tests/resolve-repositories.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,88 @@ withOrg("when requesting the same repository twice", async () => {
equal(resolvedRepos, [mockedResponse]);
});

withOrg("when requesting all repositories where one repository is ignored", async () => {
const octokit = new Octokit({ auth: "randomToken" });

const repositories = ['octoherd/*', '!octoherd/cli'];

const mockedResponse = [
{ id: 1, name: "cli", full_name: 'octoherd/cli' },
{ id: 2, name: "octokit", full_name: 'octoherd/octokit' },
{ id: 3, name: "octoherd", full_name: 'octoherd/octoherd' },
];

simple.mock(octokit, "request").resolveWith({ data: undefined });

simple.mock(octokit.paginate, "iterator").returnWith({
async *[Symbol.asyncIterator]() {
yield { data: mockedResponse };
},
});

const resolvedRepos = await resolveRepositories(
{
log: console,
octokit,
},
repositories
);

equal(resolvedRepos, [
{ id: 2, name: "octokit", full_name: 'octoherd/octokit' },
{ id: 3, name: "octoherd", full_name: 'octoherd/octoherd' },
]);
});

withOrg("when one of the requested repositories is ignored", async () => {
const octokit = new Octokit({ auth: "randomToken" });

const repositories = ['!octoherd/cli', 'octoherd/octokit'];

const mockedResponse = [
{ id: 1, name: "cli", full_name: 'octoherd/cli' },
{ id: 2, name: "octokit", full_name: 'octoherd/octokit' },
];

simple.mock(octokit, "request").resolveWith({ data: mockedResponse[0] });
simple.mock(octokit, "request").resolveWith({ data: mockedResponse[1] });

const resolvedRepos = await resolveRepositories(
{
log: console,
octokit,
},
repositories
);

equal(resolvedRepos, [
{ id: 2, name: "octokit", full_name: 'octoherd/octokit' },
]);
});

withOrg("when requested repository is ignored", async () => {
const org = "octoherd";
const repo = "cli";
const octokit = new Octokit({
auth: "randomToken",
});

const mockedResponse = { id: 1, name: repo };
const repositories = [`!${org}/${repo.toUpperCase()}`];

simple.mock(octokit, "request").resolveWith({ data: mockedResponse });

const resolvedRepos = await resolveRepositories(
{
log: console,
octokit,
},
repositories
);

equal(resolvedRepos, []);
});

withOrg("when requesting all the repositories", async () => {
const org = "octoherd";
const repo = "*";
Expand Down Expand Up @@ -305,6 +387,39 @@ withUser("when requesting all the repositories", async () => {
equal(resolvedRepos, mockedResponse);
});

withUser("when requesting all repositories where one repository is ignored", async () => {
const octokit = new Octokit({ auth: "randomToken" });

const repositories = ['*', '!gr2m/two'];

const mockedResponse = [
{ id: 1, name: "one", full_name: 'gr2m/one' },
{ id: 2, name: "two", full_name: 'gr2m/two' },
{ id: 3, name: "three", full_name: 'gr2m/three' },
];

simple.mock(octokit, "request").resolveWith({ data: undefined });

simple.mock(octokit.paginate, "iterator").returnWith({
async *[Symbol.asyncIterator]() {
yield { data: mockedResponse };
},
});

const resolvedRepos = await resolveRepositories(
{
log: console,
octokit,
},
repositories
);

equal(resolvedRepos, [
{ id: 1, name: "one", full_name: 'gr2m/one' },
{ id: 3, name: "three", full_name: 'gr2m/three' },
]);
});

resolveRepos("resolve-repositories", () => {
withOrg.run();
withUser.run();
Expand Down

0 comments on commit 49c6156

Please sign in to comment.