@zgeoff/chex 0.0.2 → 0.0.4
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 +4 -2
- package/package.json +2 -2
- package/src/build-init-package.ts +1 -1
- package/src/build-init-plan.ts +22 -4
- package/src/build-lefthook-extends.ts +5 -21
- package/src/build-lefthook-guard.ts +170 -0
- package/src/parse-lefthook-config.ts +17 -0
- package/src/read-checks-profile.ts +57 -0
- package/src/run-workflows-check.ts +11 -8
package/README.md
CHANGED
|
@@ -79,8 +79,10 @@ skipped file back:
|
|
|
79
79
|
- `prose`, `links`, `readmes` and `skill` skip each file that `.gitattributes` marks
|
|
80
80
|
`linguist-vendored`.
|
|
81
81
|
|
|
82
|
-
`chex init` writes the `extends` line
|
|
83
|
-
`chex
|
|
82
|
+
`chex init` writes the `extends` line and a
|
|
83
|
+
[`chex-baseline` job](../../docs/architecture/pre-push-baseline.md#a-checkout-without-the-baseline)
|
|
84
|
+
into `lefthook.yml`, and adds a `check:docs` script of `chex prose length links skill` when the repo
|
|
85
|
+
has none. A second run changes nothing.
|
|
84
86
|
|
|
85
87
|
## The pre-push baseline
|
|
86
88
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zgeoff/chex",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Run each shared docs and repo check as one subcommand, with the lefthook pre-push baseline that calls them",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"checks",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"ts-pattern": "5.9.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/bun": "1.4.
|
|
37
|
+
"@types/bun": "1.4.4",
|
|
38
38
|
"tiny-invariant": "1.3.3",
|
|
39
39
|
"tsrs": "0.18.0-ts7.1.0-dev.20260929",
|
|
40
40
|
"typescript": "7.0.2"
|
|
@@ -2,7 +2,7 @@ import { isRecord } from '@zgeoff/std';
|
|
|
2
2
|
|
|
3
3
|
// The baseline's jobs run chex, the related tests through rig, and each job under diet's memory cap.
|
|
4
4
|
export const baselinePackages = ['@zgeoff/chex', '@zgeoff/rig', '@zgeoff/diet'] as const;
|
|
5
|
-
export const docsScript = 'chex prose length links';
|
|
5
|
+
export const docsScript = 'chex prose length links skill';
|
|
6
6
|
|
|
7
7
|
export type PackageOutcome =
|
|
8
8
|
| { readonly kind: 'missing'; readonly packages: readonly string[] }
|
package/src/build-init-plan.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { PackageOutcome } from './build-init-package.ts';
|
|
|
3
3
|
import { buildInitPackage, docsScript } from './build-init-package.ts';
|
|
4
4
|
import type { LefthookOutcome } from './build-lefthook-extends.ts';
|
|
5
5
|
import { baselinePath, buildLefthookExtends } from './build-lefthook-extends.ts';
|
|
6
|
+
import { buildLefthookGuard, guardName, guardedHooks } from './build-lefthook-guard.ts';
|
|
6
7
|
|
|
7
8
|
export interface InitWrite {
|
|
8
9
|
readonly file: 'lefthook.yml' | 'package.json';
|
|
@@ -81,15 +82,32 @@ function buildPlanFromSteps(steps: readonly InitStep[]): InitPlan {
|
|
|
81
82
|
};
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
const keptStep: InitStep = { reasons: [], writes: [], notes: [] };
|
|
85
|
-
|
|
86
85
|
function buildLefthookStep(lefthookText: string | undefined): InitStep {
|
|
87
86
|
return match<LefthookOutcome, InitStep>(buildLefthookExtends(lefthookText))
|
|
88
|
-
.with({ kind: 'unchanged' }, () =>
|
|
87
|
+
.with({ kind: 'unchanged' }, (outcome) => buildGuardStep(outcome.text, []))
|
|
88
|
+
.with({ kind: 'changed' }, (outcome) =>
|
|
89
|
+
buildGuardStep(outcome.text, [`lefthook.yml extends ${baselinePath}`]),
|
|
90
|
+
)
|
|
91
|
+
.with({ kind: 'refused' }, (outcome) => ({ reasons: [outcome.reason], writes: [], notes: [] }))
|
|
92
|
+
.exhaustive();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const keptStep: InitStep = { reasons: [], writes: [], notes: [] };
|
|
96
|
+
|
|
97
|
+
function buildGuardStep(text: string, notes: readonly string[]): InitStep {
|
|
98
|
+
return match<LefthookOutcome, InitStep>(buildLefthookGuard(text))
|
|
99
|
+
.with({ kind: 'unchanged' }, () =>
|
|
100
|
+
notes.length === 0
|
|
101
|
+
? keptStep
|
|
102
|
+
: { reasons: [], writes: [{ file: 'lefthook.yml', text }], notes },
|
|
103
|
+
)
|
|
89
104
|
.with({ kind: 'changed' }, (outcome) => ({
|
|
90
105
|
reasons: [],
|
|
91
106
|
writes: [{ file: 'lefthook.yml', text: outcome.text }],
|
|
92
|
-
notes: [
|
|
107
|
+
notes: [
|
|
108
|
+
...notes,
|
|
109
|
+
`lefthook.yml runs ${guardName} first in ${guardedHooks.join(' and ')}, which fails when ${baselinePath} is missing`,
|
|
110
|
+
],
|
|
93
111
|
}))
|
|
94
112
|
.with({ kind: 'refused' }, (outcome) => ({ reasons: [outcome.reason], writes: [], notes: [] }))
|
|
95
113
|
.exhaustive();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseLefthookConfig } from './parse-lefthook-config.ts';
|
|
2
2
|
|
|
3
3
|
export const baselinePath = 'node_modules/@zgeoff/chex/lefthook.yml';
|
|
4
4
|
|
|
5
5
|
export type LefthookOutcome =
|
|
6
|
-
| { readonly kind: 'unchanged' }
|
|
6
|
+
| { readonly kind: 'unchanged'; readonly text: string }
|
|
7
7
|
| { readonly kind: 'changed'; readonly text: string }
|
|
8
8
|
| { readonly kind: 'refused'; readonly reason: string };
|
|
9
9
|
|
|
@@ -18,7 +18,7 @@ export function buildLefthookExtends(text: string | undefined): LefthookOutcome
|
|
|
18
18
|
return { kind: 'changed', text: `extends:\n - ${baselinePath}\n` };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const config =
|
|
21
|
+
const config = parseLefthookConfig(text);
|
|
22
22
|
|
|
23
23
|
if (config === undefined) {
|
|
24
24
|
return { kind: 'refused', reason: 'lefthook.yml is not a YAML mapping' };
|
|
@@ -31,7 +31,7 @@ export function buildLefthookExtends(text: string | undefined): LefthookOutcome
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
if (Array.isArray(extended) && extended.includes(baselinePath)) {
|
|
34
|
-
return { kind: 'unchanged' };
|
|
34
|
+
return { kind: 'unchanged', text };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const inline = inlineExtends.exec(text);
|
|
@@ -54,25 +54,9 @@ export function buildLefthookExtends(text: string | undefined): LefthookOutcome
|
|
|
54
54
|
return checkEdit(config, `${text.slice(0, end)}\n - ${baselinePath}${text.slice(end)}`);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function parseConfig(text: string): Record<string, unknown> | undefined {
|
|
58
|
-
let parsed: unknown;
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
parsed = Bun.YAML.parse(text);
|
|
62
|
-
} catch {
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (parsed === null) {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return isRecord(parsed) ? parsed : undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
57
|
// A flow mapping or a second YAML document reads differently once a block key joins the text.
|
|
74
58
|
function checkEdit(config: Record<string, unknown>, edited: string): LefthookOutcome {
|
|
75
|
-
const editedConfig =
|
|
59
|
+
const editedConfig = parseLefthookConfig(edited);
|
|
76
60
|
|
|
77
61
|
if (editedConfig !== undefined) {
|
|
78
62
|
const { extends: editedExtends, ...editedRest } = editedConfig;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { isRecord } from '@zgeoff/std';
|
|
2
|
+
import type { LefthookOutcome } from './build-lefthook-extends.ts';
|
|
3
|
+
import { baselinePath } from './build-lefthook-extends.ts';
|
|
4
|
+
import { parseLefthookConfig } from './parse-lefthook-config.ts';
|
|
5
|
+
|
|
6
|
+
export const guardedHooks = ['pre-commit', 'pre-push'] as const;
|
|
7
|
+
|
|
8
|
+
// lefthook runs the repo's own jobs before an extended file's, and a piped hook stops at the first
|
|
9
|
+
// failure, so the guard goes first.
|
|
10
|
+
export function buildLefthookGuard(text: string): LefthookOutcome {
|
|
11
|
+
let edited = text;
|
|
12
|
+
|
|
13
|
+
for (const hook of guardedHooks) {
|
|
14
|
+
const edit = buildHookEdit(edited, hook);
|
|
15
|
+
|
|
16
|
+
if (edit.kind === 'refused') {
|
|
17
|
+
return edit;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
edited = edit.text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return edited === text ? { kind: 'unchanged', text } : { kind: 'changed', text: edited };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type GuardedHook = (typeof guardedHooks)[number];
|
|
27
|
+
|
|
28
|
+
type HookEdit =
|
|
29
|
+
| { readonly kind: 'guarded'; readonly text: string }
|
|
30
|
+
| { readonly kind: 'refused'; readonly reason: string };
|
|
31
|
+
|
|
32
|
+
export const guardName = 'chex-baseline';
|
|
33
|
+
|
|
34
|
+
// lefthook drops an extends entry whose file is missing and prints nothing, so a checkout with no
|
|
35
|
+
// node_modules runs none of the baseline's jobs unless a job in the repo's own config fails.
|
|
36
|
+
const guardScript = [
|
|
37
|
+
`if [ ! -f ${baselinePath} ]; then`,
|
|
38
|
+
` echo "${guardName}: ${baselinePath} is missing, so lefthook runs none of the chex baseline's jobs; run bun install, then try again"`,
|
|
39
|
+
' exit 1',
|
|
40
|
+
'fi',
|
|
41
|
+
'# {files}',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// lefthook 2.2.2 skips a job whose run substitutes no file when no file matches: a commit that
|
|
45
|
+
// stages none, or a push of another branch while the checked-out one has nothing to push. Every
|
|
46
|
+
// repo that takes the baseline has package.json, and the shell comment hides it from the check.
|
|
47
|
+
const guardFiles = 'echo package.json';
|
|
48
|
+
const guardJob = { name: guardName, files: guardFiles, run: `${guardScript.join('\n')}\n` };
|
|
49
|
+
|
|
50
|
+
function buildHookEdit(text: string, hook: GuardedHook): HookEdit {
|
|
51
|
+
const config = parseLefthookConfig(text);
|
|
52
|
+
|
|
53
|
+
if (config === undefined) {
|
|
54
|
+
return { kind: 'refused', reason: 'lefthook.yml is not a YAML mapping' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hookConfig = config[hook];
|
|
58
|
+
|
|
59
|
+
if (hookConfig === undefined) {
|
|
60
|
+
const separator = text.endsWith('\n') ? '' : '\n';
|
|
61
|
+
|
|
62
|
+
return checkHookEdit(
|
|
63
|
+
config,
|
|
64
|
+
hook,
|
|
65
|
+
{ jobs: [guardJob] },
|
|
66
|
+
`${text}${separator}\n${hook}:\n jobs:\n${formatGuardJob(' ')}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const listedJobs = isRecord(hookConfig) ? hookConfig['jobs'] : undefined;
|
|
71
|
+
|
|
72
|
+
if (!isRecord(hookConfig) || !Array.isArray(listedJobs)) {
|
|
73
|
+
return refuseHook(hook);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const jobs: readonly unknown[] = listedJobs;
|
|
77
|
+
const listed = jobs.find((job: unknown) => isRecord(job) && job['name'] === guardName);
|
|
78
|
+
|
|
79
|
+
if (listed !== undefined) {
|
|
80
|
+
return Bun.deepEquals(listed, guardJob, true)
|
|
81
|
+
? { kind: 'guarded', text }
|
|
82
|
+
: {
|
|
83
|
+
kind: 'refused',
|
|
84
|
+
reason: `lefthook.yml has a ${guardName} job under ${hook} that differs from the one chex init writes; remove it, then run chex init again`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const insert = findGuardInsert(text, hook);
|
|
89
|
+
|
|
90
|
+
if (insert === undefined) {
|
|
91
|
+
return refuseHook(hook);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return checkHookEdit(
|
|
95
|
+
config,
|
|
96
|
+
hook,
|
|
97
|
+
{ ...hookConfig, jobs: [guardJob, ...jobs] },
|
|
98
|
+
`${text.slice(0, insert.offset)}${formatGuardJob(insert.indent)}${text.slice(insert.offset)}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// A job list that the text lays out in a form the line search misreads, such as a flow list or a
|
|
103
|
+
// key in quotes, parses to something else once the guard lines go in.
|
|
104
|
+
function checkHookEdit(
|
|
105
|
+
config: Record<string, unknown>,
|
|
106
|
+
hook: GuardedHook,
|
|
107
|
+
hookConfig: Record<string, unknown>,
|
|
108
|
+
edited: string,
|
|
109
|
+
): HookEdit {
|
|
110
|
+
const editedConfig = parseLefthookConfig(edited);
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
editedConfig !== undefined &&
|
|
114
|
+
Bun.deepEquals(editedConfig, { ...config, [hook]: hookConfig }, true)
|
|
115
|
+
) {
|
|
116
|
+
return { kind: 'guarded', text: edited };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return refuseHook(hook);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function refuseHook(hook: GuardedHook): HookEdit {
|
|
123
|
+
return {
|
|
124
|
+
kind: 'refused',
|
|
125
|
+
reason: `lefthook.yml writes ${hook} in a form chex init does not edit; write its ${hook} jobs as a block list, then run chex init again`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function formatGuardJob(indent: string): string {
|
|
130
|
+
const lines = [
|
|
131
|
+
`- name: ${guardName}`,
|
|
132
|
+
` files: ${guardFiles}`,
|
|
133
|
+
' run: |',
|
|
134
|
+
...guardScript.map((line) => ` ${line}`),
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
return lines.map((line) => `${indent}${line}\n`).join('');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface GuardInsert {
|
|
141
|
+
readonly offset: number;
|
|
142
|
+
readonly indent: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function findGuardInsert(text: string, hook: GuardedHook): GuardInsert | undefined {
|
|
146
|
+
const hookLine = new RegExp(`^${hook}:[ \\t]*(?:#.*)?$`, 'mu').exec(text);
|
|
147
|
+
|
|
148
|
+
if (hookLine === null) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const blockStart = hookLine.index + hookLine[0].length;
|
|
153
|
+
const blockEnd = /^[^\s#]/mu.exec(text.slice(blockStart + 1));
|
|
154
|
+
const blockStop = blockEnd === null ? text.length : blockStart + 1 + blockEnd.index;
|
|
155
|
+
const block = text.slice(blockStart, blockStop);
|
|
156
|
+
const jobsLine = /^[ \t]+jobs:[ \t]*(?:#.*)?$/mu.exec(block);
|
|
157
|
+
|
|
158
|
+
if (jobsLine === null) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const afterJobs = jobsLine.index + jobsLine[0].length;
|
|
163
|
+
const item = /^(?<indent>[ \t]*)- /mu.exec(block.slice(afterJobs));
|
|
164
|
+
|
|
165
|
+
if (item === null) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { offset: blockStart + afterJobs + 1, indent: item.groups?.['indent'] ?? '' };
|
|
170
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isRecord } from '@zgeoff/std';
|
|
2
|
+
|
|
3
|
+
export function parseLefthookConfig(text: string): Record<string, unknown> | undefined {
|
|
4
|
+
let parsed: unknown;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
parsed = Bun.YAML.parse(text);
|
|
8
|
+
} catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (parsed === null) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
17
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RunProgram } from './run-program.ts';
|
|
2
|
+
|
|
3
|
+
export type ChecksProfile = 'public' | 'private';
|
|
4
|
+
|
|
5
|
+
export interface ChecksProfileOptions {
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
8
|
+
readonly runProgram: RunProgram;
|
|
9
|
+
readonly writeError: (text: string) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// bun-checks picks the profile from the repo's visibility, so a run that CHECKS_PROFILE gives no
|
|
13
|
+
// profile, such as a pre-push hook, asks GitHub for the same visibility.
|
|
14
|
+
export function readChecksProfile(
|
|
15
|
+
options: Readonly<ChecksProfileOptions>,
|
|
16
|
+
): ChecksProfile | undefined {
|
|
17
|
+
const given = parseChecksProfile(options.env['CHECKS_PROFILE']);
|
|
18
|
+
|
|
19
|
+
if (given !== undefined) {
|
|
20
|
+
return given;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const visibility = readRepoVisibility(options);
|
|
24
|
+
|
|
25
|
+
if (visibility instanceof Error) {
|
|
26
|
+
options.writeError(
|
|
27
|
+
`workflows: the repo's visibility is unknown, so every rule applies: ${visibility.message}\n`,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return visibility;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseChecksProfile(value: string | undefined): ChecksProfile | undefined {
|
|
37
|
+
return value === 'public' || value === 'private' ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const visibilityCommand = ['gh', 'repo', 'view', '--json', 'isPrivate', '--jq', '.isPrivate'];
|
|
41
|
+
|
|
42
|
+
function readRepoVisibility(options: Readonly<ChecksProfileOptions>): ChecksProfile | Error {
|
|
43
|
+
try {
|
|
44
|
+
const result = options.runProgram(visibilityCommand, { cwd: options.cwd, env: options.env });
|
|
45
|
+
const answer = result.stdout.trim();
|
|
46
|
+
|
|
47
|
+
if (result.exitCode === 0 && (answer === 'true' || answer === 'false')) {
|
|
48
|
+
return answer === 'true' ? 'private' : 'public';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return new Error(
|
|
52
|
+
`${visibilityCommand.join(' ')} exited ${result.exitCode}: ${result.stderr.trim() || answer}`,
|
|
53
|
+
);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -3,11 +3,11 @@ import { collectCommandNames } from './collect-command-names.ts';
|
|
|
3
3
|
import { collectExpressionReferences } from './collect-expression-references.ts';
|
|
4
4
|
import { findInstallCachePath } from './find-install-cache-path.ts';
|
|
5
5
|
import { parseShellWords } from './parse-shell-words.ts';
|
|
6
|
+
import type { ChecksProfile } from './read-checks-profile.ts';
|
|
7
|
+
import { readChecksProfile } from './read-checks-profile.ts';
|
|
6
8
|
import type { RunProgram } from './run-program.ts';
|
|
7
9
|
import { runProgram } from './run-program.ts';
|
|
8
10
|
|
|
9
|
-
export type ChecksProfile = 'public' | 'private';
|
|
10
|
-
|
|
11
11
|
export interface WorkflowsCheckOptions {
|
|
12
12
|
readonly cwd: string;
|
|
13
13
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
@@ -15,7 +15,7 @@ export interface WorkflowsCheckOptions {
|
|
|
15
15
|
// The workflow files to read, relative to the repo root; empty reads every one HEAD holds.
|
|
16
16
|
readonly paths: readonly string[];
|
|
17
17
|
|
|
18
|
-
// The bun-checks profile of the run; undefined when
|
|
18
|
+
// The bun-checks profile of the run; undefined when it is unknown, which applies every rule.
|
|
19
19
|
readonly profile: ChecksProfile | undefined;
|
|
20
20
|
|
|
21
21
|
readonly runProgram: RunProgram;
|
|
@@ -552,7 +552,14 @@ export function runWorkflowsCommand(args: readonly string[]): number {
|
|
|
552
552
|
cwd: process.cwd(),
|
|
553
553
|
env: process.env,
|
|
554
554
|
paths,
|
|
555
|
-
profile:
|
|
555
|
+
profile: readChecksProfile({
|
|
556
|
+
cwd: process.cwd(),
|
|
557
|
+
env: process.env,
|
|
558
|
+
runProgram,
|
|
559
|
+
writeError: (text) => {
|
|
560
|
+
process.stderr.write(text);
|
|
561
|
+
},
|
|
562
|
+
}),
|
|
556
563
|
runProgram,
|
|
557
564
|
});
|
|
558
565
|
|
|
@@ -585,10 +592,6 @@ function parseCLIPaths(args: readonly string[]): string[] | Error {
|
|
|
585
592
|
}
|
|
586
593
|
}
|
|
587
594
|
|
|
588
|
-
function parseChecksProfile(value: string | undefined): ChecksProfile | undefined {
|
|
589
|
-
return value === 'public' || value === 'private' ? value : undefined;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
595
|
// GitHub reads a workflow command's properties up to `,` and `::`, and its message up to a
|
|
593
596
|
// newline, so each escapes its own characters.
|
|
594
597
|
function formatAnnotation(finding: WorkflowFinding): string {
|