workproof 0.1.0 → 0.1.1
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/dist/src/analyse.js +2 -1
- package/dist/src/figures/identity.js +14 -4
- package/dist/src/git.d.ts +3 -0
- package/dist/src/git.js +17 -0
- package/package.json +1 -1
package/dist/src/analyse.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
-
import { listCommits, listTags, rootCommit, headSha, remoteUrl } from "./git.js";
|
|
4
|
+
import { listCommits, listTags, rootCommit, headSha, remoteUrl, assertRepository } from "./git.js";
|
|
5
5
|
import { resolveIdentity } from "./figures/identity.js";
|
|
6
6
|
import { tenure, commitShare } from "./figures/commits.js";
|
|
7
7
|
import { cadence } from "./figures/cadence.js";
|
|
@@ -23,6 +23,7 @@ const survivingVersion = () => {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
export async function analyseRepo(cwd, params) {
|
|
26
|
+
await assertRepository(cwd);
|
|
26
27
|
const all = await listCommits(cwd, {});
|
|
27
28
|
const id = await resolveIdentity(all, params.author, cwd);
|
|
28
29
|
const t = tenure(all, id, { ...(params.since ? { since: params.since } : {}), ...(params.until ? { until: params.until } : {}) });
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { configuredEmail } from "../git.js";
|
|
1
|
+
import { configuredEmail, configuredName } from "../git.js";
|
|
2
2
|
/**
|
|
3
3
|
* Resolve the author to report on. `author` entries match mailmapped emails or
|
|
4
4
|
* names, case-insensitively; with none given, the repository's configured
|
|
5
5
|
* user.email is used.
|
|
6
6
|
*/
|
|
7
7
|
export async function resolveIdentity(commits, author, cwd) {
|
|
8
|
-
const
|
|
8
|
+
const explicit = author && author.length ? author : [];
|
|
9
|
+
// Without --author, try the configured email, then the configured name.
|
|
10
|
+
const wanted = (explicit.length ? explicit : [await configuredEmail(cwd), await configuredName(cwd)]).map((a) => a.toLowerCase()).filter(Boolean);
|
|
9
11
|
if (!wanted.length)
|
|
10
|
-
throw new Error(
|
|
12
|
+
throw new Error(`no author given and git config has no user.email or user.name; pass --author. ${authorsHint(commits)}`);
|
|
11
13
|
const emails = new Set();
|
|
12
14
|
const names = new Set();
|
|
13
15
|
for (const c of commits) {
|
|
@@ -17,7 +19,15 @@ export async function resolveIdentity(commits, author, cwd) {
|
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
if (!emails.size)
|
|
20
|
-
throw new Error(`no commits by ${wanted.join("
|
|
22
|
+
throw new Error(`no commits by ${wanted.join(" or ")} in this repository. ${authorsHint(commits)}`);
|
|
21
23
|
return { emails: [...emails].sort(), names: [...names].sort() };
|
|
22
24
|
}
|
|
25
|
+
/** The most frequent author names, so the error tells the user what to pass. */
|
|
26
|
+
function authorsHint(commits) {
|
|
27
|
+
const counts = new Map();
|
|
28
|
+
for (const c of commits)
|
|
29
|
+
counts.set(c.name, (counts.get(c.name) ?? 0) + 1);
|
|
30
|
+
const top = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name, n]) => `"${name}" (${n})`);
|
|
31
|
+
return top.length ? `Authors here: ${top.join(", ")}. Pass one with --author.` : "The repository has no commits.";
|
|
32
|
+
}
|
|
23
33
|
export const isMine = (c, id) => id.emails.includes(c.email);
|
package/dist/src/git.d.ts
CHANGED
|
@@ -26,4 +26,7 @@ export declare function listTags(cwd: string): Promise<Tag[]>;
|
|
|
26
26
|
export declare const rootCommit: (cwd: string) => Promise<string>;
|
|
27
27
|
export declare const headSha: (cwd: string) => Promise<string>;
|
|
28
28
|
export declare function remoteUrl(cwd: string): Promise<string>;
|
|
29
|
+
export declare function configuredName(cwd: string): Promise<string>;
|
|
30
|
+
/** Throws a plain sentence when the directory is not inside a git repository. */
|
|
31
|
+
export declare function assertRepository(cwd: string): Promise<void>;
|
|
29
32
|
export declare function configuredEmail(cwd: string): Promise<string>;
|
package/dist/src/git.js
CHANGED
|
@@ -66,6 +66,23 @@ export async function remoteUrl(cwd) {
|
|
|
66
66
|
return "";
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
export async function configuredName(cwd) {
|
|
70
|
+
try {
|
|
71
|
+
return (await git(["config", "--get", "user.name"], cwd)).trim();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Throws a plain sentence when the directory is not inside a git repository. */
|
|
78
|
+
export async function assertRepository(cwd) {
|
|
79
|
+
try {
|
|
80
|
+
await git(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new Error(`${cwd} is not inside a git repository (use --repo to point at one)`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
69
86
|
export async function configuredEmail(cwd) {
|
|
70
87
|
try {
|
|
71
88
|
return (await git(["config", "--get", "user.email"], cwd)).trim().toLowerCase();
|
package/package.json
CHANGED