@avi2dg/checks 0.30.0 → 0.32.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 +17 -0
- package/README.md +5 -2
- package/dist/readability/index.js +94 -1
- package/docs/configs/commit-messages.md +5 -0
- package/docs/configs/typescript-rules.md +10 -0
- package/docs/design.md +38 -4
- package/docs/gates/checks-commit-identity.md +3 -0
- package/docs/gates/checks-lint-coverage.md +9 -8
- package/docs/gates/checks-lint.md +3 -2
- package/docs/gates/checks-release-notes.md +10 -1
- package/docs/gates/checks-release-pr.md +185 -0
- package/docs/gates/checks-release-report.md +4 -2
- package/docs/gates/checks-release-tag.md +63 -0
- package/docs/gates/checks-secrets.md +93 -0
- package/docs/gates/checks-unused.md +7 -6
- package/oxlintrc.json +7 -0
- package/package.json +22 -3
- package/src/complexity/exports.ts +2 -1
- package/src/complexity/knip.ts +4 -5
- package/src/complexity/unused.ts +4 -3
- package/src/core/gates.ts +7 -4
- package/src/delivery/commit-identity.ts +18 -2
- package/src/delivery/github.ts +38 -0
- package/src/delivery/gitleaks.toml +113 -0
- package/src/delivery/gitleaks.ts +111 -0
- package/src/delivery/release-pr.ts +195 -0
- package/src/delivery/release-report.ts +3 -30
- package/src/delivery/release-tag.ts +64 -0
- package/src/delivery/release.ts +79 -0
- package/src/delivery/secrets.ts +86 -0
- package/src/dependencies/osv-scanner.ts +3 -45
- package/src/dependencies/pinned-binary.ts +79 -0
- package/src/quality/lint-coverage.sh +11 -6
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Crypto, Effect, Encoding, FileSystem, Path, Schema } from "effect";
|
|
2
|
+
import { HttpClient, HttpClientResponse } from "effect/unstable/http";
|
|
3
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
4
|
+
import { collect } from "../core/git.ts";
|
|
5
|
+
|
|
6
|
+
const EXECUTABLE_MODE = 0o755;
|
|
7
|
+
|
|
8
|
+
export type Asset =
|
|
9
|
+
| { readonly kind: "binary"; readonly url: string; readonly sha256: string }
|
|
10
|
+
| { readonly kind: "tar.gz"; readonly url: string; readonly sha256: string; readonly member: string; readonly memberSha256: string };
|
|
11
|
+
|
|
12
|
+
class PinnedBinaryError extends Schema.TaggedError<PinnedBinaryError>()("PinnedBinaryError", {
|
|
13
|
+
message: Schema.String,
|
|
14
|
+
}) {}
|
|
15
|
+
|
|
16
|
+
function installedSha256(asset: Asset): string {
|
|
17
|
+
return asset.kind === "binary" ? asset.sha256 : asset.memberSha256;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sha256Of = Effect.fn("sha256Of")(function* (bytes: Uint8Array) {
|
|
21
|
+
const crypto = yield* Crypto.Crypto;
|
|
22
|
+
return Encoding.encodeHex(yield* crypto.digest("SHA-256", bytes));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const verified = Effect.fn("verified")(function* (binary: string, sha256: string) {
|
|
26
|
+
const fs = yield* FileSystem.FileSystem;
|
|
27
|
+
const found = yield* sha256Of(yield* fs.readFile(binary));
|
|
28
|
+
if (found !== sha256) {
|
|
29
|
+
return yield* new PinnedBinaryError({ message: `${binary} has SHA-256 ${found}, not the pinned ${sha256}; delete it and rerun` });
|
|
30
|
+
}
|
|
31
|
+
return binary;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const download = Effect.fn("download")(function* (url: string) {
|
|
35
|
+
const response = yield* HttpClient.get(url).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk));
|
|
36
|
+
return new Uint8Array(yield* response.arrayBuffer);
|
|
37
|
+
}, Effect.provide(FetchHttpClient.layer));
|
|
38
|
+
|
|
39
|
+
const unpacked = Effect.fn("unpacked")(function* (asset: Asset, bytes: Uint8Array, staging: string) {
|
|
40
|
+
const fs = yield* FileSystem.FileSystem;
|
|
41
|
+
const path = yield* Path.Path;
|
|
42
|
+
const staged = path.join(staging, path.basename(asset.url));
|
|
43
|
+
yield* fs.writeFile(staged, bytes);
|
|
44
|
+
if (asset.kind === "binary") return staged;
|
|
45
|
+
const { stderr, exitCode } = yield* collect("tar", ["-xzf", staged, "-C", staging, asset.member]).pipe(
|
|
46
|
+
Effect.mapError((cause) => new PinnedBinaryError({ message: `cannot run tar: ${cause.message}` })),
|
|
47
|
+
);
|
|
48
|
+
if (exitCode !== 0) {
|
|
49
|
+
return yield* new PinnedBinaryError({ message: `cannot unpack ${asset.member} from ${asset.url}: ${stderr.trim()}` });
|
|
50
|
+
}
|
|
51
|
+
const member = path.join(staging, asset.member);
|
|
52
|
+
const found = yield* sha256Of(yield* fs.readFile(member));
|
|
53
|
+
if (found !== asset.memberSha256) {
|
|
54
|
+
return yield* new PinnedBinaryError({
|
|
55
|
+
message: `${asset.member} in ${asset.url} has SHA-256 ${found}, not the pinned ${asset.memberSha256}, so nothing was installed`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return member;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// The binary lands through a rename in its own directory, so a concurrent run sees it whole or not at all.
|
|
62
|
+
export const installPinned = Effect.fn("installPinned")(function* (asset: Asset, binary: string) {
|
|
63
|
+
const fs = yield* FileSystem.FileSystem;
|
|
64
|
+
const path = yield* Path.Path;
|
|
65
|
+
if (yield* fs.exists(binary)) return yield* verified(binary, installedSha256(asset));
|
|
66
|
+
const bytes = yield* download(asset.url).pipe(
|
|
67
|
+
Effect.mapError((cause) => new PinnedBinaryError({ message: `cannot download ${asset.url}: ${cause.message}` })),
|
|
68
|
+
);
|
|
69
|
+
const found = yield* sha256Of(bytes);
|
|
70
|
+
if (found !== asset.sha256) {
|
|
71
|
+
return yield* new PinnedBinaryError({ message: `${asset.url} has SHA-256 ${found}, not the pinned ${asset.sha256}, so nothing was installed` });
|
|
72
|
+
}
|
|
73
|
+
yield* fs.makeDirectory(path.dirname(binary), { recursive: true });
|
|
74
|
+
const staging = yield* fs.makeTempDirectoryScoped({ directory: path.dirname(binary), prefix: `.${path.basename(binary)}-` });
|
|
75
|
+
const staged = yield* unpacked(asset, bytes, staging);
|
|
76
|
+
yield* fs.chmod(staged, EXECUTABLE_MODE);
|
|
77
|
+
yield* fs.rename(staged, binary);
|
|
78
|
+
return binary;
|
|
79
|
+
}, Effect.scoped);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# lint-coverage: fail when oxlint silently skips a tracked TypeScript source,
|
|
2
|
+
# lint-coverage: fail when oxlint silently skips a tracked TypeScript or Astro source,
|
|
3
3
|
# or when tsconfig.json's program silently drops the ts-reset rules.
|
|
4
4
|
set -eu
|
|
5
5
|
|
|
6
6
|
tmp="$(mktemp -d)"
|
|
7
7
|
trap 'rm -rf "$tmp"' EXIT INT TERM
|
|
8
8
|
|
|
9
|
-
git ls-files -- '*.ts' '*.tsx' | LC_ALL=C sort > "$tmp/expected"
|
|
9
|
+
git ls-files -- '*.ts' '*.tsx' '*.astro' | LC_ALL=C sort > "$tmp/expected"
|
|
10
10
|
if ! [ -s "$tmp/expected" ]; then
|
|
11
|
-
echo "lint-coverage: no tracked .ts/.tsx files"
|
|
11
|
+
echo "lint-coverage: no tracked .ts/.tsx/.astro files"
|
|
12
12
|
exit 0
|
|
13
13
|
fi
|
|
14
14
|
|
|
@@ -18,7 +18,7 @@ if ! oxlint --debug=files > "$tmp/walk" 2> "$tmp/walk-error"; then
|
|
|
18
18
|
cat "$tmp/walk" "$tmp/walk-error"
|
|
19
19
|
exit 2
|
|
20
20
|
fi
|
|
21
|
-
grep -E '
|
|
21
|
+
grep -E '[.]tsx?$|[.]astro$' "$tmp/walk" | LC_ALL=C sort > "$tmp/walked" || true
|
|
22
22
|
|
|
23
23
|
expected_count="$(wc -l < "$tmp/expected" | tr -d ' ')"
|
|
24
24
|
walked_count="$(grep -c . "$tmp/walked" || true)"
|
|
@@ -27,11 +27,16 @@ status=0
|
|
|
27
27
|
comm -23 "$tmp/expected" "$tmp/walked" > "$tmp/missing"
|
|
28
28
|
if [ -s "$tmp/missing" ]; then
|
|
29
29
|
missing_count="$(wc -l < "$tmp/missing" | tr -d ' ')"
|
|
30
|
-
echo "lint-coverage: oxlint skips ${missing_count}/${expected_count} tracked .ts/.tsx files; missing:"
|
|
30
|
+
echo "lint-coverage: oxlint skips ${missing_count}/${expected_count} tracked .ts/.tsx/.astro files; missing:"
|
|
31
31
|
cat "$tmp/missing"
|
|
32
32
|
status=1
|
|
33
33
|
else
|
|
34
|
-
echo "lint-coverage: ${walked_count}/${expected_count} tracked .ts/.tsx files"
|
|
34
|
+
echo "lint-coverage: ${walked_count}/${expected_count} tracked .ts/.tsx/.astro files"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if ! grep -qE '[.]tsx?$' "$tmp/expected"; then
|
|
38
|
+
echo "lint-coverage: no tracked .ts/.tsx files, so no program to hold the ts-reset rules"
|
|
39
|
+
exit "$status"
|
|
35
40
|
fi
|
|
36
41
|
|
|
37
42
|
if ! [ -f tsconfig.json ]; then
|