@shivaedev/quality 0.3.0 → 0.3.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/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/dist/git/base.d.ts.map +1 -1
- package/dist/git/base.js +29 -4
- package/dist/git/base.js.map +1 -1
- package/dist/git/command.d.ts +1 -1
- package/dist/git/command.d.ts.map +1 -1
- package/dist/git/command.js +2 -2
- package/dist/git/command.js.map +1 -1
- package/package.json +1 -1
- package/src/git/base.ts +40 -5
- package/src/git/command.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 - 2026-10-03
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `quality baseline check` fetches the full history with `git fetch --unshallow`
|
|
8
|
+
when a shallow clone does not reach the merge base, and looks for it again
|
|
9
|
+
before it exits 2. The fetch never stops to ask for a
|
|
10
|
+
password.
|
|
11
|
+
|
|
3
12
|
## 0.3.0 - 2026-10-03
|
|
4
13
|
|
|
5
14
|
### Added
|
package/README.md
CHANGED
|
@@ -252,7 +252,7 @@ quality baseline check
|
|
|
252
252
|
|
|
253
253
|
It compares with the merge base, never the tip of the target branch, so a branch that is behind never fails for debt the target branch paid off since. An entry whose file git sees as moved since the merge base is compared with the entry at the old path. When the base holds the earlier JSON format at `quality/baseline.json`, the check converts it the way `migrate` does, so the change that migrates the baseline passes.
|
|
254
254
|
|
|
255
|
-
The check reads git, not the sources, so it is cheap enough for every commit; only a base in the earlier format makes it run the rules. The target is `--against <ref>`, or else `origin/HEAD`, `origin/main` and then `origin/master`, whichever exists first. It exits 2 when there is no git work tree, no target or no merge base
|
|
255
|
+
The check reads git, not the sources, so it is cheap enough for every commit; only a base in the earlier format makes it run the rules. The target is `--against <ref>`, or else `origin/HEAD`, `origin/main` and then `origin/master`, whichever exists first. In a shallow clone that does not reach the merge base, it runs `git fetch --unshallow`, which never stops to ask for a password, and looks again. It exits 2 when there is no git work tree, no target or no merge base, including when that fetch fails; then check out with `fetch-depth: 0` in GitHub Actions, or fetch enough history for `git merge-base HEAD <target>` to succeed.
|
|
256
256
|
|
|
257
257
|
### Adopting a rule
|
|
258
258
|
|
package/dist/git/base.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/git/base.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/git/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAsED,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,CAW1G,CAAC"}
|
package/dist/git/base.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
2
|
import { SetupFailure } from "../failure.js";
|
|
3
3
|
import { git } from "./command.js";
|
|
4
4
|
const FALLBACK_BRANCHES = ["origin/main", "origin/master"];
|
|
@@ -16,9 +16,35 @@ const defaultBranch = (root) => Effect.gen(function* () {
|
|
|
16
16
|
}
|
|
17
17
|
return yield* fail("cannot find the default branch: origin/HEAD, origin/main and origin/master are missing. Pass --against <ref>, or run `git remote set-head origin --auto`.");
|
|
18
18
|
});
|
|
19
|
-
const
|
|
19
|
+
const isShallow = (root) => Effect.map(git(root, ["rev-parse", "--is-shallow-repository"]), (result) => result.stdout.trim() === "true");
|
|
20
|
+
const mergeBase = (root, ref) => Effect.map(git(root, ["merge-base", "HEAD", ref]), (result) => (result.code === 0 ? result.stdout.trim() : undefined));
|
|
21
|
+
const remoteOf = (root, ref) => Effect.gen(function* () {
|
|
22
|
+
const name = (yield* git(root, ["rev-parse", "--symbolic-full-name", ref])).stdout.trim();
|
|
23
|
+
const remotes = (yield* git(root, ["remote"])).stdout.split("\n");
|
|
24
|
+
return remotes.filter((remote) => remote !== "" && name.startsWith(`refs/remotes/${remote}/`)).slice(0, 1);
|
|
25
|
+
});
|
|
26
|
+
const unshallow = (root, ref) => Effect.gen(function* () {
|
|
27
|
+
const remote = yield* remoteOf(root, ref);
|
|
28
|
+
yield* Console.error(`quality: the clone is shallow; fetching its full history to find the merge base of HEAD and ${ref}.`);
|
|
29
|
+
yield* git(root, ["fetch", "--quiet", "--unshallow", ...remote], { GIT_TERMINAL_PROMPT: "0" });
|
|
30
|
+
});
|
|
31
|
+
const missingMergeBase = (root, ref) => Effect.flatMap(isShallow(root), (shallow) => fail(shallow
|
|
20
32
|
? `the clone is shallow and does not reach the merge base of HEAD and ${ref}. Fetch the history first: \`git fetch --unshallow\`, or fetch-depth: 0 in actions/checkout.`
|
|
21
33
|
: `HEAD and ${ref} share no history.`));
|
|
34
|
+
const reachMergeBase = (root, ref) => Effect.gen(function* () {
|
|
35
|
+
const found = yield* mergeBase(root, ref);
|
|
36
|
+
if (found !== undefined) {
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
if (yield* isShallow(root)) {
|
|
40
|
+
yield* unshallow(root, ref);
|
|
41
|
+
const deepened = yield* mergeBase(root, ref);
|
|
42
|
+
if (deepened !== undefined) {
|
|
43
|
+
return deepened;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return yield* missingMergeBase(root, ref);
|
|
47
|
+
});
|
|
22
48
|
export const resolveBase = (root, against) => Effect.gen(function* () {
|
|
23
49
|
const inside = yield* git(root, ["rev-parse", "--is-inside-work-tree"]);
|
|
24
50
|
if (inside.code !== 0 || inside.stdout.trim() !== "true") {
|
|
@@ -28,7 +54,6 @@ export const resolveBase = (root, against) => Effect.gen(function* () {
|
|
|
28
54
|
if (!(yield* isCommit(root, ref))) {
|
|
29
55
|
return yield* fail(`${ref} names no commit in this clone. Fetch it first, or pass another --against <ref>.`);
|
|
30
56
|
}
|
|
31
|
-
|
|
32
|
-
return merged.code === 0 ? { commit: merged.stdout.trim(), ref } : yield* missingMergeBase(root, ref);
|
|
57
|
+
return { commit: yield* reachMergeBase(root, ref), ref };
|
|
33
58
|
});
|
|
34
59
|
//# sourceMappingURL=base.js.map
|
package/dist/git/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/git/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/git/base.ts"],"names":[],"mappings":"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"}
|
package/dist/git/command.d.ts
CHANGED
|
@@ -7,6 +7,6 @@ export interface GitResult {
|
|
|
7
7
|
readonly stderr: string;
|
|
8
8
|
}
|
|
9
9
|
export type Git = ChildProcessSpawner.ChildProcessSpawner;
|
|
10
|
-
export declare const git: (cwd: string, args: ReadonlyArray<string
|
|
10
|
+
export declare const git: (cwd: string, args: ReadonlyArray<string>, env?: Readonly<Record<string, string>>) => Effect.Effect<GitResult, SetupFailure, Git>;
|
|
11
11
|
export declare const gitOrFail: (cwd: string, args: ReadonlyArray<string>) => Effect.Effect<string, SetupFailure, Git>;
|
|
12
12
|
//# sourceMappingURL=command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/git/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,GAAG,GAAG,mBAAmB,CAAC,mBAAmB,CAAC;AAE1D,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/git/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,GAAG,GAAG,mBAAmB,CAAC,mBAAmB,CAAC;AAE1D,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,CAW3C,CAAC;AAEvG,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAK1G,CAAC"}
|
package/dist/git/command.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Effect, Stream } from "effect";
|
|
2
2
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
3
3
|
import { SetupFailure } from "../failure.js";
|
|
4
|
-
export const git = (cwd, args) => Effect.scoped(Effect.gen(function* () {
|
|
4
|
+
export const git = (cwd, args, env) => Effect.scoped(Effect.gen(function* () {
|
|
5
5
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
6
|
-
const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, stdin: "ignore" }));
|
|
6
|
+
const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, env, extendEnv: true, stdin: "ignore" }));
|
|
7
7
|
const [stdout, stderr, code] = yield* Effect.all([Stream.mkString(Stream.decodeText(child.stdout)), Stream.mkString(Stream.decodeText(child.stderr)), child.exitCode], { concurrency: "unbounded" });
|
|
8
8
|
return { code: Number(code), stderr, stdout };
|
|
9
9
|
})).pipe(Effect.mapError((error) => new SetupFailure({ message: `cannot run git: ${error.message}` })));
|
package/dist/git/command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/git/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAU7C,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,IAA2B,EAA+C,EAAE,
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/git/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAU7C,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,IAA2B,EAAE,GAAsC,EAA+C,EAAE,CACpJ,MAAM,CAAC,MAAM,CACZ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAC;IAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;IACxH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAC/C,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,EACpH,EAAE,WAAW,EAAE,WAAW,EAAE,CAC5B,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC/C,CAAC,CAAC,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,mBAAmB,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AAEvG,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,IAA2B,EAA4C,EAAE,CAC/G,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CACzC,MAAM,CAAC,IAAI,KAAK,CAAC;IAChB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;IAC/B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,aAAa,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAC7H,CAAC"}
|
package/package.json
CHANGED
package/src/git/base.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Console, Effect } from "effect";
|
|
2
2
|
import { SetupFailure } from "../failure.ts";
|
|
3
3
|
import { type Git, git } from "./command.ts";
|
|
4
4
|
|
|
@@ -30,15 +30,51 @@ const defaultBranch = (root: string): Effect.Effect<string, SetupFailure, Git> =
|
|
|
30
30
|
);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
const isShallow = (root: string): Effect.Effect<boolean, SetupFailure, Git> =>
|
|
34
|
+
Effect.map(git(root, ["rev-parse", "--is-shallow-repository"]), (result) => result.stdout.trim() === "true");
|
|
35
|
+
|
|
36
|
+
const mergeBase = (root: string, ref: string): Effect.Effect<string | undefined, SetupFailure, Git> =>
|
|
37
|
+
Effect.map(git(root, ["merge-base", "HEAD", ref]), (result) => (result.code === 0 ? result.stdout.trim() : undefined));
|
|
38
|
+
|
|
39
|
+
const remoteOf = (root: string, ref: string): Effect.Effect<ReadonlyArray<string>, SetupFailure, Git> =>
|
|
40
|
+
Effect.gen(function* () {
|
|
41
|
+
const name = (yield* git(root, ["rev-parse", "--symbolic-full-name", ref])).stdout.trim();
|
|
42
|
+
const remotes = (yield* git(root, ["remote"])).stdout.split("\n");
|
|
43
|
+
return remotes.filter((remote) => remote !== "" && name.startsWith(`refs/remotes/${remote}/`)).slice(0, 1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const unshallow = (root: string, ref: string): Effect.Effect<void, SetupFailure, Git> =>
|
|
47
|
+
Effect.gen(function* () {
|
|
48
|
+
const remote = yield* remoteOf(root, ref);
|
|
49
|
+
yield* Console.error(`quality: the clone is shallow; fetching its full history to find the merge base of HEAD and ${ref}.`);
|
|
50
|
+
yield* git(root, ["fetch", "--quiet", "--unshallow", ...remote], { GIT_TERMINAL_PROMPT: "0" });
|
|
51
|
+
});
|
|
52
|
+
|
|
33
53
|
const missingMergeBase = (root: string, ref: string): Effect.Effect<never, SetupFailure, Git> =>
|
|
34
|
-
Effect.flatMap(
|
|
54
|
+
Effect.flatMap(isShallow(root), (shallow) =>
|
|
35
55
|
fail(
|
|
36
|
-
shallow
|
|
56
|
+
shallow
|
|
37
57
|
? `the clone is shallow and does not reach the merge base of HEAD and ${ref}. Fetch the history first: \`git fetch --unshallow\`, or fetch-depth: 0 in actions/checkout.`
|
|
38
58
|
: `HEAD and ${ref} share no history.`,
|
|
39
59
|
),
|
|
40
60
|
);
|
|
41
61
|
|
|
62
|
+
const reachMergeBase = (root: string, ref: string): Effect.Effect<string, SetupFailure, Git> =>
|
|
63
|
+
Effect.gen(function* () {
|
|
64
|
+
const found = yield* mergeBase(root, ref);
|
|
65
|
+
if (found !== undefined) {
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
if (yield* isShallow(root)) {
|
|
69
|
+
yield* unshallow(root, ref);
|
|
70
|
+
const deepened = yield* mergeBase(root, ref);
|
|
71
|
+
if (deepened !== undefined) {
|
|
72
|
+
return deepened;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return yield* missingMergeBase(root, ref);
|
|
76
|
+
});
|
|
77
|
+
|
|
42
78
|
export const resolveBase = (root: string, against: string | undefined): Effect.Effect<Base, SetupFailure, Git> =>
|
|
43
79
|
Effect.gen(function* () {
|
|
44
80
|
const inside = yield* git(root, ["rev-parse", "--is-inside-work-tree"]);
|
|
@@ -49,6 +85,5 @@ export const resolveBase = (root: string, against: string | undefined): Effect.E
|
|
|
49
85
|
if (!(yield* isCommit(root, ref))) {
|
|
50
86
|
return yield* fail(`${ref} names no commit in this clone. Fetch it first, or pass another --against <ref>.`);
|
|
51
87
|
}
|
|
52
|
-
|
|
53
|
-
return merged.code === 0 ? { commit: merged.stdout.trim(), ref } : yield* missingMergeBase(root, ref);
|
|
88
|
+
return { commit: yield* reachMergeBase(root, ref), ref };
|
|
54
89
|
});
|
package/src/git/command.ts
CHANGED
|
@@ -10,11 +10,11 @@ export interface GitResult {
|
|
|
10
10
|
|
|
11
11
|
export type Git = ChildProcessSpawner.ChildProcessSpawner;
|
|
12
12
|
|
|
13
|
-
export const git = (cwd: string, args: ReadonlyArray<string
|
|
13
|
+
export const git = (cwd: string, args: ReadonlyArray<string>, env?: Readonly<Record<string, string>>): Effect.Effect<GitResult, SetupFailure, Git> =>
|
|
14
14
|
Effect.scoped(
|
|
15
15
|
Effect.gen(function* () {
|
|
16
16
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
17
|
-
const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, stdin: "ignore" }));
|
|
17
|
+
const child = yield* spawner.spawn(ChildProcess.make("git", [...args], { cwd, env, extendEnv: true, stdin: "ignore" }));
|
|
18
18
|
const [stdout, stderr, code] = yield* Effect.all(
|
|
19
19
|
[Stream.mkString(Stream.decodeText(child.stdout)), Stream.mkString(Stream.decodeText(child.stderr)), child.exitCode],
|
|
20
20
|
{ concurrency: "unbounded" },
|