@avi2dg/checks 0.22.0 → 0.24.0
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 +20 -0
- package/CONTRIBUTING.md +6 -5
- package/README.md +4 -1
- package/dist/effect-channel/index.js +122 -0
- package/dist/{index.js → readability/index.js} +8 -120
- package/docs/configs/native-settings.md +2 -2
- package/docs/design.md +7 -2
- package/docs/gates/checks-changelog.md +57 -0
- package/docs/gates/checks-release-notes.md +141 -0
- package/docs/gates/checks-release-report.md +64 -0
- package/docs/gates/checks-repetition.md +1 -0
- package/docs/gates/checks-vendor.md +11 -2
- package/oxlintrc.json +1 -1
- package/package.json +18 -4
- package/scripts/changelog-write.ts +95 -0
- package/scripts/changelog.ts +110 -0
- package/scripts/release-notes.ts +37 -0
- package/scripts/release-report.ts +50 -0
- package/scripts/repetition.ts +14 -3
- package/scripts/vendor-args.ts +54 -0
- package/scripts/vendor.ts +36 -66
package/scripts/vendor.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Option, Path, Schema } from "effect";
|
|
3
3
|
import { git } from "./git.ts";
|
|
4
|
-
import { runMain
|
|
4
|
+
import { runMain } from "./main.ts";
|
|
5
|
+
import { librariesFrom, NAME, OPENER, type Library } from "./vendor-args.ts";
|
|
5
6
|
|
|
6
|
-
const NAME = "checks-vendor";
|
|
7
|
-
const USAGE = `usage: ${NAME} [--library <name> --package <package> --repository <remote> --tag <template> [--path <manifest>]]...`;
|
|
8
7
|
const CACHE_HOME = ".cache/avi2dg-checks";
|
|
9
8
|
const RECORD_SUFFIX = ".commit";
|
|
10
9
|
const LINKS = "repos";
|
|
@@ -21,55 +20,6 @@ export class Unreachable extends Schema.TaggedError<Unreachable>()("Unreachable"
|
|
|
21
20
|
message: Schema.String,
|
|
22
21
|
}) {}
|
|
23
22
|
|
|
24
|
-
const Library = Schema.Struct({
|
|
25
|
-
name: Schema.String.check(Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)),
|
|
26
|
-
package: Schema.NonEmptyString,
|
|
27
|
-
repository: Schema.NonEmptyString,
|
|
28
|
-
tag: Schema.String.check(Schema.isPattern(/\{version\}/)),
|
|
29
|
-
path: Schema.optionalKey(Schema.String.check(Schema.isPattern(/^(?:[\w.@+-]+\/)*[\w.@+-]+\.\w+$/))),
|
|
30
|
-
});
|
|
31
|
-
type Library = typeof Library.Type;
|
|
32
|
-
|
|
33
|
-
const Libraries = Schema.Array(Library).check(
|
|
34
|
-
Schema.makeFilter((libraries) => {
|
|
35
|
-
const names = libraries.map(({ name }) => name);
|
|
36
|
-
const repeated = names.find((name, index) => names.indexOf(name) !== index);
|
|
37
|
-
return repeated === undefined || `--library ${repeated} appears more than once`;
|
|
38
|
-
}),
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
const OPENER = "--library";
|
|
42
|
-
const FIELDS = new Map<string, keyof Library>([
|
|
43
|
-
["--package", "package"],
|
|
44
|
-
["--repository", "repository"],
|
|
45
|
-
["--tag", "tag"],
|
|
46
|
-
["--path", "path"],
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
function misuse(message: string): Usage {
|
|
50
|
-
return new Usage({ message: `${message}\n${USAGE}` });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const librariesFrom = Effect.fn("librariesFrom")(function* (args: readonly string[]) {
|
|
54
|
-
const groups: Record<string, string>[] = [];
|
|
55
|
-
for (let index = 0; index < args.length; index += 2) {
|
|
56
|
-
const flag = args[index] ?? "";
|
|
57
|
-
const value = args[index + 1];
|
|
58
|
-
if (value === undefined || value.startsWith("--")) return yield* misuse(`${flag} takes a value`);
|
|
59
|
-
if (flag === OPENER) {
|
|
60
|
-
groups.push({ name: value });
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
const field = FIELDS.get(flag);
|
|
64
|
-
const current = groups.at(-1);
|
|
65
|
-
if (field === undefined) return yield* misuse(`${flag} is not an argument`);
|
|
66
|
-
if (current === undefined) return yield* misuse(`${flag} comes before any ${OPENER}`);
|
|
67
|
-
if (field in current) return yield* misuse(`${flag} appears twice for ${OPENER} ${current["name"]}`);
|
|
68
|
-
current[field] = value;
|
|
69
|
-
}
|
|
70
|
-
return yield* Schema.decodeUnknownEffect(Libraries)(groups).pipe(Effect.mapError((cause) => misuse(cause.message)));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
23
|
export function tagFor(template: string, version: string): string {
|
|
74
24
|
return template.replaceAll(VERSION_TOKEN, version);
|
|
75
25
|
}
|
|
@@ -171,6 +121,11 @@ function clearing(dir: string): string {
|
|
|
171
121
|
return `clear it with \`chmod -R u+w ${dir} && rm -rf ${dir}\` and rerun ${NAME}`;
|
|
172
122
|
}
|
|
173
123
|
|
|
124
|
+
function writableAt(dir: string, paths: readonly string[], by: string): string {
|
|
125
|
+
const [first = dir] = paths;
|
|
126
|
+
return `${paths.length === 1 ? "1 path" : `${paths.length} paths`} writable by ${by}, starting with ${first}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
174
129
|
const freeze = Effect.fn("freeze")(function* (dir: string) {
|
|
175
130
|
const fs = yield* FileSystem.FileSystem;
|
|
176
131
|
for (const { entry, mode } of yield* modes(dir)) {
|
|
@@ -212,7 +167,8 @@ const recorded = Effect.fn("recorded")(function* (dir: string) {
|
|
|
212
167
|
return Option.some(text.trim());
|
|
213
168
|
});
|
|
214
169
|
|
|
215
|
-
|
|
170
|
+
// Returns the paths still carrying the owner write bit and fails on every other finding, reading the status only once none are left.
|
|
171
|
+
const inspect = Effect.fn("inspect")(function* (dir: string, library: Library, installed: string, tag: string) {
|
|
216
172
|
const held = yield* recorded(dir);
|
|
217
173
|
if (Option.isNone(held)) {
|
|
218
174
|
return yield* new VendorError({ message: `${recordOf(dir)} is missing, so ${dir} has no commit to hold to; ${clearing(dir)}` });
|
|
@@ -223,13 +179,13 @@ const verify = Effect.fn("verify")(function* (dir: string, library: Library, ins
|
|
|
223
179
|
return yield* new VendorError({ message: `${dir} sits on ${head}, not the recorded ${record}; ${clearing(dir)}` });
|
|
224
180
|
}
|
|
225
181
|
yield* checkVersion(dir, library, installed, tag);
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return yield* new VendorError({
|
|
230
|
-
message: `${dir} leaves ${tampered.length} paths writable, starting with ${first}; ${clearing(dir)}`,
|
|
231
|
-
});
|
|
182
|
+
const found = yield* modes(dir);
|
|
183
|
+
const shared = found.filter(({ mode }) => (mode & WRITE_BITS & ~OWNER_WRITE) !== 0).map(({ entry }) => entry);
|
|
184
|
+
if (shared.length > 0) {
|
|
185
|
+
return yield* new VendorError({ message: `${dir} leaves ${writableAt(dir, shared, "group or others")}; ${clearing(dir)}` });
|
|
232
186
|
}
|
|
187
|
+
const writable = found.filter(({ mode }) => (mode & OWNER_WRITE) !== 0).map(({ entry }) => entry);
|
|
188
|
+
if (writable.length > 0) return writable;
|
|
233
189
|
const status = yield* git(["status", "--porcelain", "--ignored"], dir).pipe(
|
|
234
190
|
Effect.mapError((cause) => new VendorError({ message: `${dir} reports no status: ${cause.message}` })),
|
|
235
191
|
);
|
|
@@ -237,6 +193,7 @@ const verify = Effect.fn("verify")(function* (dir: string, library: Library, ins
|
|
|
237
193
|
const [first = ""] = status.trim().split("\n");
|
|
238
194
|
return yield* new VendorError({ message: `${dir} holds writes outside the recorded commit, starting with ${first}; ${clearing(dir)}` });
|
|
239
195
|
}
|
|
196
|
+
return writable;
|
|
240
197
|
});
|
|
241
198
|
|
|
242
199
|
const ensureLink = Effect.fn("ensureLink")(function* (root: string, library: Library, dir: string) {
|
|
@@ -315,8 +272,26 @@ const land = Effect.fn("land")(function* (library: Library, installed: string, t
|
|
|
315
272
|
return yield* stage(staging, library, installed, tag, dir).pipe(Effect.ensuring(discard(staging)));
|
|
316
273
|
});
|
|
317
274
|
|
|
318
|
-
|
|
275
|
+
function verified(library: Library, tag: string): string {
|
|
276
|
+
return `${LINKS}/${library.name} still holds ${tag}, verified against its recorded commit`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const settle = Effect.fn("settle")(function* (library: Library, installed: string, tag: string, dir: string) {
|
|
319
280
|
const fs = yield* FileSystem.FileSystem;
|
|
281
|
+
if (!(yield* fs.exists(dir)) && (yield* land(library, installed, tag, dir))) {
|
|
282
|
+
return `cloned ${tag} from ${library.repository} and linked ${LINKS}/${library.name}`;
|
|
283
|
+
}
|
|
284
|
+
const writable = yield* inspect(dir, library, installed, tag);
|
|
285
|
+
if (writable.length === 0) return verified(library, tag);
|
|
286
|
+
yield* freeze(dir);
|
|
287
|
+
const left = yield* inspect(dir, library, installed, tag);
|
|
288
|
+
if (left.length > 0) {
|
|
289
|
+
return yield* new VendorError({ message: `${dir} leaves ${writableAt(dir, left, "its owner")}; ${clearing(dir)}` });
|
|
290
|
+
}
|
|
291
|
+
return `found ${writableAt(dir, writable, "its owner")}, and froze the tree again, so ${verified(library, tag)}`;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const vend = Effect.fn("vend")(function* (root: string, cache: string, library: Library) {
|
|
320
295
|
const path = yield* Path.Path;
|
|
321
296
|
const installed = yield* manifestVersion(
|
|
322
297
|
path.join(root, "node_modules", ...library.package.split("/"), "package.json"),
|
|
@@ -324,12 +299,7 @@ const vend = Effect.fn("vend")(function* (root: string, cache: string, library:
|
|
|
324
299
|
);
|
|
325
300
|
const tag = tagFor(library.tag, installed);
|
|
326
301
|
const dir = path.join(cache, LINKS, ...remoteSegments(library.repository), tag);
|
|
327
|
-
|
|
328
|
-
yield* Console.error(`${NAME}: cloned ${tag} from ${library.repository} and linked ${LINKS}/${library.name}`);
|
|
329
|
-
} else {
|
|
330
|
-
yield* verify(dir, library, installed, tag);
|
|
331
|
-
yield* Console.error(`${NAME}: ${LINKS}/${library.name} still holds ${tag}, verified against its recorded commit`);
|
|
332
|
-
}
|
|
302
|
+
yield* Console.error(`${NAME}: ${yield* settle(library, installed, tag, dir)}`);
|
|
333
303
|
yield* ensureLink(root, library, dir);
|
|
334
304
|
});
|
|
335
305
|
|