github-reporadar 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/assets/index-Bb1rtvKk.js +73 -0
- package/dist/index.html +1 -1
- package/dist-cli/cli.js +85 -44
- package/package.json +1 -1
- package/dist/assets/index-7rO9STC2.js +0 -73
package/dist/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap"
|
|
17
17
|
rel="stylesheet"
|
|
18
18
|
/>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-Bb1rtvKk.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-B5UAXuco.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/dist-cli/cli.js
CHANGED
|
@@ -195,24 +195,41 @@ async function fetchLogin(token) {
|
|
|
195
195
|
const { json } = await rest(token, "/user");
|
|
196
196
|
return json.login;
|
|
197
197
|
}
|
|
198
|
-
async function listRecentRepos(token, since, includePrivate) {
|
|
199
|
-
const repos = [];
|
|
198
|
+
async function listRecentRepos(token, since, includePrivate, orgs = []) {
|
|
200
199
|
const visibility = "";
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
200
|
+
const urls = [
|
|
201
|
+
`/user/repos?sort=pushed&per_page=100&affiliation=owner${visibility}`,
|
|
202
|
+
// --orgで足した組織。トークンが見える範囲(type=all)をpushed降順で
|
|
203
|
+
...orgs.map((o) => `/orgs/${encodeURIComponent(o)}/repos?sort=pushed&per_page=100&type=all`)
|
|
204
|
+
];
|
|
205
|
+
const seen = /* @__PURE__ */ new Set();
|
|
206
|
+
const repos = [];
|
|
207
|
+
for (const start of urls) {
|
|
208
|
+
let url = start;
|
|
209
|
+
while (url) {
|
|
210
|
+
const { json, next } = await rest(token, url);
|
|
211
|
+
const page = json;
|
|
212
|
+
for (const r of page) {
|
|
213
|
+
if (!seen.has(r.full_name)) {
|
|
214
|
+
seen.add(r.full_name);
|
|
215
|
+
repos.push(r);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (page.length === 0 || new Date(page[page.length - 1].pushed_at) < since) break;
|
|
219
|
+
url = next;
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
222
|
return repos.filter((r) => {
|
|
210
223
|
return new Date(r.pushed_at) >= since;
|
|
211
224
|
});
|
|
212
225
|
}
|
|
213
|
-
|
|
226
|
+
function commitsUrl(fullName, since, author) {
|
|
227
|
+
const base = `/repos/${fullName}/commits?since=${since.toISOString()}&per_page=100`;
|
|
228
|
+
return author ? `${base}&author=${encodeURIComponent(author)}` : base;
|
|
229
|
+
}
|
|
230
|
+
async function listCommits(token, repo, since, author) {
|
|
214
231
|
const commits = [];
|
|
215
|
-
let url =
|
|
232
|
+
let url = commitsUrl(repo.full_name, since, author);
|
|
216
233
|
let pages = 0;
|
|
217
234
|
while (url && pages < MAX_COMMIT_PAGES) {
|
|
218
235
|
let result;
|
|
@@ -257,33 +274,44 @@ ${fields}
|
|
|
257
274
|
})));
|
|
258
275
|
return result;
|
|
259
276
|
}
|
|
260
|
-
|
|
277
|
+
function issuesUrls(fullName, login) {
|
|
278
|
+
const base = `/repos/${fullName}/issues?state=open&per_page=100`;
|
|
279
|
+
if (!login) return [base];
|
|
280
|
+
const me = encodeURIComponent(login);
|
|
281
|
+
return [`${base}&creator=${me}`, `${base}&assignee=${me}`];
|
|
282
|
+
}
|
|
283
|
+
async function listOpenIssues(token, repo, login) {
|
|
261
284
|
const items = [];
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
let
|
|
266
|
-
|
|
267
|
-
result
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
285
|
+
const seen = /* @__PURE__ */ new Set();
|
|
286
|
+
for (const start of issuesUrls(repo.full_name, login)) {
|
|
287
|
+
let url = start;
|
|
288
|
+
let pages = 0;
|
|
289
|
+
while (url && pages < 5) {
|
|
290
|
+
let result;
|
|
291
|
+
try {
|
|
292
|
+
result = await rest(token, url);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
if (err instanceof Error && err.message.includes("HTTP 409")) return [];
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
for (const it of result.json) {
|
|
298
|
+
if (seen.has(it.number)) continue;
|
|
299
|
+
seen.add(it.number);
|
|
300
|
+
items.push({
|
|
301
|
+
repo: repo.full_name,
|
|
302
|
+
type: it.pull_request ? "pr" : "issue",
|
|
303
|
+
number: it.number,
|
|
304
|
+
title: it.title,
|
|
305
|
+
state: it.state,
|
|
306
|
+
labels: it.labels.map((l) => l.name),
|
|
307
|
+
updatedAt: it.updated_at,
|
|
308
|
+
url: it.html_url,
|
|
309
|
+
comments: it.comments
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
url = result.next;
|
|
313
|
+
pages++;
|
|
284
314
|
}
|
|
285
|
-
url = result.next;
|
|
286
|
-
pages++;
|
|
287
315
|
}
|
|
288
316
|
return items;
|
|
289
317
|
}
|
|
@@ -312,8 +340,9 @@ async function fetchActivity(token, from, opts) {
|
|
|
312
340
|
const limit = createLimiter(CONCURRENCY);
|
|
313
341
|
const [login, recentRepos] = await Promise.all([
|
|
314
342
|
fetchLogin(token),
|
|
315
|
-
listRecentRepos(token, from)
|
|
343
|
+
listRecentRepos(token, from, opts.includePrivate, opts.orgs ?? [])
|
|
316
344
|
]);
|
|
345
|
+
const mineOnly = (r) => r.owner.login.toLowerCase() === login.toLowerCase() ? null : login;
|
|
317
346
|
const openCounts = await fetchOpenCounts(token, recentRepos.map((r) => r.full_name), limit);
|
|
318
347
|
const issueRepos = recentRepos.filter((r) => {
|
|
319
348
|
const c = openCounts.get(r.full_name);
|
|
@@ -322,7 +351,7 @@ async function fetchActivity(token, from, opts) {
|
|
|
322
351
|
const progress = createProgress(recentRepos.length, issueRepos.length);
|
|
323
352
|
const commitsPromise = Promise.all(
|
|
324
353
|
recentRepos.map(
|
|
325
|
-
(r) => limit(() => listCommits(token, r, from)).then((commits) => {
|
|
354
|
+
(r) => limit(() => listCommits(token, r, from, mineOnly(r))).then((commits) => {
|
|
326
355
|
progress.repoDone();
|
|
327
356
|
return commits;
|
|
328
357
|
})
|
|
@@ -330,7 +359,7 @@ async function fetchActivity(token, from, opts) {
|
|
|
330
359
|
);
|
|
331
360
|
const issuesPromise = Promise.all(
|
|
332
361
|
issueRepos.map(
|
|
333
|
-
(r) => limit(() => listOpenIssues(token, r)).then((items) => {
|
|
362
|
+
(r) => limit(() => listOpenIssues(token, r, mineOnly(r))).then((items) => {
|
|
334
363
|
progress.issueDone();
|
|
335
364
|
return items;
|
|
336
365
|
})
|
|
@@ -382,9 +411,11 @@ async function runSync(opts) {
|
|
|
382
411
|
const from = new Date(to.getTime() - windowDays * 24 * 60 * 60 * 1e3);
|
|
383
412
|
log(`GitHub RepoRadar sync: ${from.toISOString().slice(0, 10)} 〜 ${to.toISOString().slice(0, 10)} (${windowDays}日)`);
|
|
384
413
|
const token = getToken();
|
|
385
|
-
const
|
|
414
|
+
const orgs = opts.orgs ?? [];
|
|
415
|
+
if (orgs.length > 0) log(`組織も取得: ${orgs.join(", ")}(自分がauthorのコミットだけ数える)`);
|
|
416
|
+
const { login, repos, activity, issues } = await fetchActivity(token, from, { includePrivate: true, orgs });
|
|
386
417
|
mkdirSync(opts.dataDir, { recursive: true });
|
|
387
|
-
const meta = { lastSyncAt: to.toISOString(), login, windowDays, includesPrivate: true };
|
|
418
|
+
const meta = { lastSyncAt: to.toISOString(), login, windowDays, includesPrivate: true, ...orgs.length > 0 ? { orgs } : {} };
|
|
388
419
|
writeFileSync(join(opts.dataDir, "repos.json"), JSON.stringify(repos, null, 2));
|
|
389
420
|
writeFileSync(join(opts.dataDir, "activity.json"), JSON.stringify(activity, null, 2));
|
|
390
421
|
writeFileSync(join(opts.dataDir, "issues.json"), JSON.stringify(issues, null, 2));
|
|
@@ -414,7 +445,8 @@ const DEFAULTS = {
|
|
|
414
445
|
dataDir: null,
|
|
415
446
|
includePrivate: false,
|
|
416
447
|
sort: "active",
|
|
417
|
-
limit: 15
|
|
448
|
+
limit: 15,
|
|
449
|
+
orgs: []
|
|
418
450
|
};
|
|
419
451
|
function intIn(flag, raw, min, max) {
|
|
420
452
|
if (raw === void 0 || raw.startsWith("--")) throw new RangeError(`${flag} には値が要ります`);
|
|
@@ -425,7 +457,7 @@ function intIn(flag, raw, min, max) {
|
|
|
425
457
|
return n;
|
|
426
458
|
}
|
|
427
459
|
function parseArgs(argv) {
|
|
428
|
-
const out = { ...DEFAULTS };
|
|
460
|
+
const out = { ...DEFAULTS, orgs: [] };
|
|
429
461
|
let command = null;
|
|
430
462
|
for (let i = 0; i < argv.length; i++) {
|
|
431
463
|
const a = argv[i];
|
|
@@ -458,6 +490,14 @@ function parseArgs(argv) {
|
|
|
458
490
|
out.dataDir = v;
|
|
459
491
|
break;
|
|
460
492
|
}
|
|
493
|
+
case "--org": {
|
|
494
|
+
const v = argv[++i];
|
|
495
|
+
if (v === void 0 || v.startsWith("--")) throw new RangeError(`${a}には値が要ります`);
|
|
496
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9-]*$/.test(v)) throw new RangeError(`--orgはGitHubの組織名(英数字とハイフン)で指定してください(受け取った値: ${v})`);
|
|
497
|
+
if (out.orgs.includes(v)) throw new RangeError(`--org ${v}が重複しています`);
|
|
498
|
+
out.orgs.push(v);
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
461
501
|
case "--sort": {
|
|
462
502
|
const v = argv[++i];
|
|
463
503
|
if (!SORTS.includes(v)) throw new RangeError(`--sortは${SORTS.join(" | ")} のどれか(受け取った値: ${v})`);
|
|
@@ -491,6 +531,7 @@ const USAGE = [
|
|
|
491
531
|
row("--port N", "待ち受けポート(既定5177。使用中なら次を探す)"),
|
|
492
532
|
row("--no-open", "ブラウザを開かない"),
|
|
493
533
|
row("--no-sync", "同期せずに手元のデータで開く"),
|
|
534
|
+
row("--org NAME", "組織のrepoも取得する(繰り返し可)。組織では自分がauthorのコミットと、自分が作ったか担当のissue/PRだけ数える"),
|
|
494
535
|
row("--data-dir DIR", "データの置き場(既定: $XDG_CACHE_HOME か ~/.cache の github-reporadar/data)"),
|
|
495
536
|
row("--sort KEY", "brief の並び: active | recent | stale"),
|
|
496
537
|
row("--limit N", "brief の件数(既定15)"),
|
|
@@ -625,7 +666,7 @@ async function main() {
|
|
|
625
666
|
}
|
|
626
667
|
const dataDir = args.dataDir ?? defaultDataDir(process.env, homedir());
|
|
627
668
|
if (args.command === "sync" || args.command === "serve" && args.sync) {
|
|
628
|
-
await runSync({ dataDir, days: args.days });
|
|
669
|
+
await runSync({ dataDir, days: args.days, orgs: args.orgs });
|
|
629
670
|
}
|
|
630
671
|
if (args.command === "sync") return;
|
|
631
672
|
if (args.command === "brief") {
|