Skip to content

Commit 8d8f42a

Browse files
authored
feat: improved logging, writing of debug.log file. ES Module export. Improved Types. (#11)
1 parent bcc3554 commit 8d8f42a

9 files changed

+538
-248
lines changed

README.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@
77
## Usage
88

99
```
10-
$ octoherd [script] [repos...]
10+
$ octoherd.js [script] [repos...]
1111
1212
Positionals:
1313
script Path to your *.js script
14-
repos One or multiple arrays in the form of 'repo-owner/repo-name'
15-
[default: []]
14+
repos One or multiple arrays in the form of 'repo-owner/repo-name'.
15+
'repo-owner/*' will find all repositories for one owner. '*' will find
16+
all repositories the user has access to [default: []]
1617
1718
Options:
18-
--help Show help [boolean]
19-
--version Show version number [boolean]
20-
--token Requires the "public_repo" scope for public repositories, "repo"
21-
scope for private repositories. [string] [required]
22-
--cache Cache responses for debugging [boolean] [default: false]
19+
--help Show help [boolean]
20+
--version Show version number [boolean]
21+
--octoherd-token Requires the "public_repo" scope for public repositories,
22+
"repo" scope for private repositories. [string] [required]
23+
--octoherd-cache Cache responses for debugging [boolean] [default: false]
2324
```
2425

2526
The `script` must export a `script` function which takes three parameters:

bin/octoherd.js

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,44 @@
11
#!/usr/bin/env node
22

3-
const yargs = require("yargs");
3+
import yargs from "yargs";
44

5-
const octoherd = require("..");
5+
import { octoherd } from "../index.js";
66

7-
var argv = yargs
7+
const argv = yargs
88
.usage("Usage: $0 [options] [script] [repos...]")
99
.example(
1010
"$0 --token 0123456789012345678901234567890123456789 octokit/rest.js"
1111
)
1212
.command("$0 [script] [repos...]", "", (yargs) => {
1313
yargs.positional("script", {
1414
demandOption: true,
15-
describe: "Path to your *.js script",
15+
describe: "Path to *.js script. Must be an ES Module.",
1616
});
1717
yargs.positional("repos", {
1818
demandOption: true,
19-
describe: "One or multiple arrays in the form of 'repo-owner/repo-name'",
19+
describe:
20+
"One or multiple arrays 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",
2021
default: [],
2122
});
2223
})
23-
.option("token", {
24+
.option("octoherd-token", {
2425
description:
2526
'Requires the "public_repo" scope for public repositories, "repo" scope for private repositories.',
2627
demandOption: true,
2728
type: "string",
2829
})
29-
.option("cache", {
30+
.option("octoherd-cache", {
3031
description: "Cache responses for debugging",
3132
type: "boolean",
3233
default: false,
3334
})
3435
.epilog("copyright 2020").argv;
3536

36-
const { _, $0, ...options } = argv;
37-
octoherd(options).catch(console.error);
37+
const { _, $0, script, repos, ...options } = argv;
38+
39+
octoherd({ ...options, octoherdScript: script, octoherdRepos: repos }).catch(
40+
(error) => {
41+
console.error(error);
42+
process.exit(1);
43+
}
44+
);

index.d.ts

Whitespace-only changes.

index.js

+77-51
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,110 @@
1-
module.exports = octoherd;
1+
import { resolve } from "path";
2+
import { appendFileSync } from "fs";
23

3-
const { resolve } = require("path");
4+
import { Octokit } from "@octoherd/octokit";
5+
import chalk from "chalk";
6+
import tempy from "tempy";
47

5-
const { Octokit: OctokitCore } = require("@octokit/core");
6-
const { paginateRest } = require("@octokit/plugin-paginate-rest");
7-
const { throttling } = require("@octokit/plugin-throttling");
8-
const { retry } = require("@octokit/plugin-retry");
9-
const pino = require("pino");
8+
import { cache as octokitCachePlugin } from "./lib/octokit-plugin-cache.js";
9+
import { resolveRepositories } from "./lib/resolve-repositories.js";
10+
import { VERSION } from "./version.js";
1011

11-
const { cache: octokitCachePlugin } = require("./lib/octokit-plugin-cache");
12-
const { resolveRepositories } = require("./lib/resolve-repositories");
13-
const { name, version } = require("./package.json");
14-
15-
const logger = pino();
16-
const Octokit = OctokitCore.plugin(paginateRest, throttling, retry).defaults({
17-
log: {
18-
debug: logger.debug.bind(logger),
19-
info: logger.info.bind(logger),
20-
warn: logger.warn.bind(logger),
21-
error: logger.error.bind(logger),
22-
},
23-
userAgent: [name, version].join("/"),
24-
throttle: {
25-
onAbuseLimit: (error, options, octokit) => {
26-
octokit.log.error("onAbuseLimit", error, options);
27-
},
28-
onRateLimit: (error, options, octokit) => {
29-
octokit.log.error("onRateLimit", error, options);
30-
},
31-
},
32-
});
12+
const levelColor = {
13+
debug: chalk.bgGray.black,
14+
info: chalk.bgGreen.black,
15+
warn: chalk.bgYellow.black,
16+
error: chalk.bgRed.white.bold,
17+
};
3318

3419
/**
3520
* Find all releases in a GitHub repository or organization after a specified date
3621
*
3722
* @param {object} options
38-
* @param {string} options.token Personal Access Token: Requires the "public_repo" scope for public repositories, "repo" scope for private repositories.
39-
* @param {string} options.script Path to script to run against a repository
40-
* @param {string} options.repos Array of repository names in the form of "repo-owner/repo-name". To match all repositories for an owner, pass "repo-owner/*"
41-
* @param {boolean} options.cache Cache responses for debugging
23+
* @param {string} options.octoherdToken Personal Access Token: Requires the "public_repo" scope for public repositories, "repo" scope for private repositories.
24+
* @param {string} options.octoherdScript Path to script to run against a repository
25+
* @param {string} options.octoherdCache Array of repository names in the form of "repo-owner/repo-name". To match all repositories for an owner, pass "repo-owner/*"
26+
* @param {boolean} options.octoherdRepos Cache responses for debugging
4227
*/
43-
async function octoherd(
28+
export async function octoherd(
4429
options = {
45-
cache: false,
46-
repos: [],
30+
octoherdCache: false,
31+
octoherdRepos: [],
4732
}
4833
) {
49-
const { token, script, repos, cache, ...userOptions } = options;
50-
const MyOctokit = cache ? Octokit.plugin(octokitCachePlugin) : Octokit;
51-
const octokit = new MyOctokit({
52-
auth: token,
34+
const {
35+
octoherdToken,
36+
octoherdScript,
37+
octoherdCache,
38+
octoherdRepos,
39+
...userOptions
40+
} = options;
41+
const tmpLogFile = tempy.file({ extension: "ndjson.log" });
42+
43+
const CliOctokit = octoherdCache
44+
? Octokit.plugin(octokitCachePlugin)
45+
: Octokit;
46+
const octokit = new CliOctokit({
47+
auth: octoherdToken,
48+
userAgent: ["octoherd-cli", VERSION].join("/"),
49+
octoherd: {
50+
debug: true,
51+
onLogMessage(level, message, additionalData) {
52+
console.log(
53+
levelColor[level](" " + level.toUpperCase() + " "),
54+
Object.keys(additionalData).length
55+
? `${message} ${chalk.gray(JSON.stringify(additionalData))}`
56+
: message
57+
);
58+
},
59+
onLogData(data) {
60+
appendFileSync(tmpLogFile, JSON.stringify(data));
61+
},
62+
},
5363
});
5464

5565
let userScript;
56-
const path = resolve(process.cwd(), script);
66+
const path = resolve(process.cwd(), octoherdScript);
67+
5768
try {
58-
userScript = require(path).script;
69+
userScript = (await import(path)).script;
5970
} catch (error) {
60-
throw new Error(`[octoherd] ${script} script could not be found`);
71+
throw new Error(`[octoherd] ${octoherdScript} script could not be found`);
6172
}
6273

6374
if (!userScript) {
6475
throw new Error(`[octoherd] no "script" exported at ${path}`);
6576
}
6677

67-
if (repos.length === 0) {
78+
if (octoherdCache.length === 0) {
6879
throw new Error("[octoherd] No repositories provided");
6980
}
7081

71-
state = {
82+
const state = {
7283
log: console,
7384
octokit,
7485
};
7586

76-
const repositories = await resolveRepositories(state, repos);
87+
try {
88+
const repositories = await resolveRepositories(state, octoherdRepos);
7789

78-
for (const repository of repositories) {
79-
octokit.log.info("Running %s on %s...", script, repository.full_name);
80-
await userScript(octokit, repository, userOptions);
81-
}
90+
for (const repository of repositories) {
91+
octokit.log.info(
92+
"Running %s on %s...",
93+
octoherdScript,
94+
repository.full_name
95+
);
96+
await userScript(octokit, repository, userOptions);
97+
}
8298

83-
octokit.log.info("done");
99+
console.log("");
100+
console.log(levelColor.info(" DONE "), `Log file written to ${tmpLogFile}`);
101+
} catch (error) {
102+
octokit.log.error(error);
103+
console.log("");
104+
console.log(
105+
levelColor.error(" DONE "),
106+
`Log file written to ${tmpLogFile}`
107+
);
108+
process.exit(1);
109+
}
84110
}

lib/octokit-plugin-cache.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
module.exports = { cache };
1+
import { URL } from "url";
2+
import { dirname } from "path";
23

3-
const { URL } = require("url");
4-
const { dirname } = require("path");
4+
import mkdirp from "mkdirp";
5+
import jsonfile from "jsonfile";
56

6-
const mkdirp = require("mkdirp");
7-
const { readFileSync, writeFileSync } = require("jsonfile");
8-
9-
function cache(octokit) {
7+
export function cache(octokit) {
108
octokit.hook.wrap("request", async (request, options) => {
119
if (options.method !== "GET") {
1210
return request(options);
@@ -19,12 +17,12 @@ function cache(octokit) {
1917
const cachePath = `./cache${pathname}${page ? `-page-${page}` : ""}.json`;
2018

2119
try {
22-
return readFileSync(cachePath);
20+
return jsonfile.readFileSync(cachePath);
2321
} catch (error) {
2422
const response = await request(options);
2523

2624
mkdirp.sync(dirname(cachePath));
27-
writeFileSync(cachePath, response);
25+
jsonfile.writeFileSync(cachePath, response);
2826
return response;
2927
}
3028
});

lib/resolve-repositories.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
module.exports = { resolveRepositories };
2-
3-
async function resolveRepositories(state, repositories) {
1+
export async function resolveRepositories(state, repositories) {
42
const invalidRepositories = repositories.filter((fullName) => {
53
/^[a-z0-9_.-]+\/([a-z0-9_.-]+|\*)$/i.test(fullName);
64
});

0 commit comments

Comments
 (0)