diff --git a/index.js b/index.js index de6c730..4c816a3 100644 --- a/index.js +++ b/index.js @@ -10,10 +10,11 @@ import enquirer from "enquirer"; import { cache as octokitCachePlugin } from "./lib/octokit-plugin-cache.js"; import { requestLog } from "./lib/octokit-plugin-request-log.js"; import { requestConfirm } from "./lib/octokit-plugin-request-confirm.js"; -import { runScriptAgainstRepositories } from "./lib/run-script-against-repositories.js"; +import { loadRepositoriesAndRunScript } from "./lib/load-repositories-and-run-script.js"; import { VERSION } from "./version.js"; export { Octokit } from "@octoherd/octokit"; +export { runScriptAgainstRepositories } from "./lib/run-script-against-repositories.js"; const levelColor = { debug: chalk.bgGray.black, @@ -113,7 +114,7 @@ export async function octoherd(options) { octoherdReposPassedAsFlag: !!octoherdRepos, }; - await runScriptAgainstRepositories(state, octoherdRepos); + await loadRepositoriesAndRunScript(state, octoherdRepos); console.log(""); console.log(chalk.gray("-".repeat(80))); diff --git a/lib/load-repositories-and-run-script.js b/lib/load-repositories-and-run-script.js new file mode 100644 index 0000000..c953893 --- /dev/null +++ b/lib/load-repositories-and-run-script.js @@ -0,0 +1,62 @@ +import enquirer from "enquirer"; +import chalk from "chalk"; + +import { resolveRepositories } from "./resolve-repositories.js"; +import { runScriptAgainstRepositories } from "./run-script-against-repositories.js"; + +export async function loadRepositoriesAndRunScript(state, octoherdRepos = []) { + if (!state.octoherdReposPassedAsFlag) { + console.log(""); + const prompt = new enquirer.List({ + message: "Enter repositories", + separator: / +/, + hint: + "e.g. octoherd/cli. Use a * to load all repositories for an owner, e.g. octoherd/*. Enter nothing to exit", + validate(input) { + const values = typeof input === "string" ? [input] : input; + + const invalid = values.find((value) => { + if (value.trim() === "*") return; + + if (/^!?[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.*-]+$/.test(value.trim())) { + return; + } + + return true; + }); + + if (!invalid) return true; + + return ( + chalk.red(`"${invalid}" is not a valid repository name.`) + + chalk.gray(" The format is /") + ); + }, + }); + + octoherdRepos = await prompt.run(); + + if (!state.reposNoticeShown) { + console.log( + `${chalk.gray( + "To avoid this prompt in future, pass repositories with --octoherd-repos or -R" + )}\n` + ); + } + state.reposNoticeShown = true; + } + + if (octoherdRepos.length === 0) return; + + try { + state.octokit.log.info("Loading repositories ..."); + const repositories = await resolveRepositories(state, octoherdRepos); + + await runScriptAgainstRepositories(state.octokit, repositories, state.script, state.userOptions); + } catch (error) { + state.octokit.log.error(error); + process.exitCode = 1; + } + + await loadRepositoriesAndRunScript(state); +} diff --git a/lib/run-script-against-repositories.js b/lib/run-script-against-repositories.js index 868009d..2b4f3c6 100644 --- a/lib/run-script-against-repositories.js +++ b/lib/run-script-against-repositories.js @@ -1,76 +1,21 @@ -import enquirer from "enquirer"; -import chalk from "chalk"; - -import { resolveRepositories } from "./resolve-repositories.js"; - -export async function runScriptAgainstRepositories(state, octoherdRepos = []) { - if (!state.octoherdReposPassedAsFlag) { - console.log(""); - const prompt = new enquirer.List({ - message: "Enter repositories", - separator: / +/, - hint: - "e.g. octoherd/cli. Use a * to load all repositories for an owner, e.g. octoherd/*. Enter nothing to exit", - validate(input) { - const values = typeof input === "string" ? [input] : input; - - const invalid = values.find((value) => { - if (value.trim() === "*") return; - - if (/^!?[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.*-]+$/.test(value.trim())) { - return; - } - - return true; - }); - - if (!invalid) return true; - - return ( - chalk.red(`"${invalid}" is not a valid repository name.`) + - chalk.gray(" The format is /") - ); - }, - }); - - octoherdRepos = await prompt.run(); - - if (!state.reposNoticeShown) { - console.log( - `${chalk.gray( - "To avoid this prompt in future, pass repositories with --octoherd-repos or -R" - )}\n` - ); - } - state.reposNoticeShown = true; - } - - if (octoherdRepos.length === 0) return; - - try { - state.octokit.log.info("Loading repositories ..."); - const repositories = await resolveRepositories(state, octoherdRepos); - - for (const repository of repositories) { - state.octokit.log.info( - { octoherd: true }, - "Running on %s ...", - repository.full_name - ); - - try { - const { id, owner, name } = repository; - state.octokit.log.setContext({ repository: { id, owner, name } }); - await state.script(state.octokit, repository, state.userOptions); - } catch (error) { - if (!error.cancel) throw error; - state.octokit.log.debug(error.message); +export async function runScriptAgainstRepositories(octokit, repositories, script, options) { + for (const repository of repositories) { + octokit.log.info( + { octoherd: true }, + "Running on %s ...", + repository.full_name + ); + + try { + if (octokit.log.setContext) { + const {id, owner, name} = repository; + octokit.log.setContext({repository: {id, owner, name}}); } + + await script(octokit, repository, options); + } catch (error) { + if (!error.cancel) throw error; + octokit.log.debug(error.message); } - } catch (error) { - state.octokit.log.error(error); - process.exitCode = 1; } - - await runScriptAgainstRepositories(state); } diff --git a/tests/run-script-against-resolved-repositories.test.js b/tests/run-script-against-resolved-repositories.test.js new file mode 100644 index 0000000..ee97799 --- /dev/null +++ b/tests/run-script-against-resolved-repositories.test.js @@ -0,0 +1,69 @@ +import { suite } from "uvu"; +import { equal } from "uvu/assert"; +import simple from "simple-mock"; + +import { runScriptAgainstRepositories } from "../lib/run-script-against-repositories.js"; + +const runAgainstRepos = suite('run script against repositories'); + +const userOptions = {}; +const octoherdCliRepository = {id: 123, owner: {login: 'octoherd'}, name: 'cli', full_name: 'octoherd/cli'}; + +runAgainstRepos('run the script against the provided repositories', async () => { + const octokitInfoLogger = simple.spy(() => undefined); + const octokit = {log: {info: octokitInfoLogger}}; + const octokitCoreRepository = {id: 456, owner: {login: 'octokit'}, name: 'core.js', full_name: 'octokit/core.js'}; + const repositories = [octoherdCliRepository, octokitCoreRepository]; + const script = simple.spy(() => undefined); + + await runScriptAgainstRepositories(octokit, repositories, script, userOptions); + + repositories.forEach((repository, index) => { + equal(script.calls[index].args, [octokit, repository, userOptions]); + equal(octokitInfoLogger.calls[index].args, [{octoherd: true}, "Running on %s ...", repository.full_name]); + }); +}); + +runAgainstRepos('set logger context when available', async () => { + const octokitLoggerContextSetter = simple.spy(() => undefined); + const octokit = {log: {info: () => undefined, setContext: octokitLoggerContextSetter}}; + const octokitCoreRepository = {id: 456, owner: {login: 'octokit'}, name: 'core.js', full_name: 'octokit/core.js'}; + const repositories = [octoherdCliRepository, octokitCoreRepository]; + const script = simple.spy(() => undefined); + + await runScriptAgainstRepositories(octokit, repositories, script, userOptions); + + repositories.forEach((repository, index) => equal( + octokitLoggerContextSetter.calls[index].args, + [{repository: {id: repository.id, owner: repository.owner, name: repository.name}}] + )); +}); + +runAgainstRepos('log cancelled routes at debug level', async () => { + const repositories = [octoherdCliRepository]; + const errorMessage = 'error message'; + const cancelledRouteError = new Error(errorMessage); + cancelledRouteError.cancel = true; + const octokitDebugLogger = simple.spy(() => undefined); + const octokit = {log: {info: () => undefined, debug: octokitDebugLogger}}; + const script = simple.spy(() => undefined).throwWith(cancelledRouteError); + + await runScriptAgainstRepositories(octokit, repositories, script, userOptions); + + equal(octokitDebugLogger.calls[0].args, [errorMessage]); +}); + +runAgainstRepos('throw other errors', async () => { + const repositories = [octoherdCliRepository]; + const error = new Error(); + const octokit = {log: {info: () => undefined}}; + const script = simple.spy(() => undefined).throwWith(error); + + try { + await runScriptAgainstRepositories(octokit, repositories, script, userOptions); + } catch (err) { + equal(err, error); + } +}); + +runAgainstRepos.run();