@starci/hfs 5.0.4 → 5.0.6
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 +10 -0
- package/README.md +22 -2
- package/cache/app-families.d.mts +8 -5
- package/cache/app-families.mjs +11 -9
- package/cache/hfs-family.d.mts +2 -0
- package/cache/hfs-family.mjs +4 -4
- package/cache/run.d.mts +2 -0
- package/cache/run.mjs +2 -5
- package/cache/session.mjs +1 -1
- package/cache/test-family.d.mts +6 -4
- package/cache/test-family.mjs +22 -14
- package/cache/types-family.d.mts +5 -3
- package/cache/types-family.mjs +9 -8
- package/cache/wiring-family.d.mts +4 -2
- package/cache/wiring-family.mjs +5 -5
- package/ci/cli.d.mts +7 -1
- package/ci/cli.mjs +37 -6
- package/ci/github.d.mts +1 -0
- package/ci/secret.d.mts +10 -1
- package/ci/secret.mjs +26 -6
- package/ci/verify.d.mts +9 -1
- package/ci/verify.mjs +5 -3
- package/emit/contracts.d.mts +12 -9
- package/emit/contracts.mjs +29 -11
- package/lint/chunk.mjs +11 -2
- package/lint/run.d.mts +2 -1
- package/lint/run.mjs +26 -29
- package/package.json +1 -1
- package/release/candidate-ci.mjs +1 -1
- package/release/gate.d.mts +1 -0
- package/release/gate.mjs +76 -18
- package/release/index.d.mts +1 -1
- package/release/index.mjs +7 -3
- package/runtime/knowledge/grammars/index.yaml +11 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +6 -6
- package/runtime/knowledge/hfs/rules.yaml +2 -2
- package/runtime/modules/kernel/failure-codes.yaml +4 -4
- package/runtime/modules/schemas/application-stacks.schema.yaml +10 -7
- package/runtime/scripts/api/node/run-node.d.mts +29 -0
- package/runtime/scripts/api/node/run-node.mjs +19 -0
- package/runtime/scripts/gates/starcistacks-ci.mjs +3 -1
- package/runtime/scripts/gates/starcistacks-form.d.mts +1 -0
- package/runtime/scripts/gates/starcistacks.d.mts +1 -0
- package/runtime/scripts/gates/starcistacks.mjs +22 -10
- package/runtime/scripts/hfs/app-wiring.d.mts +8 -2
- package/runtime/scripts/hfs/app-wiring.mjs +27 -7
- package/runtime/scripts/hfs/architecture/contract-bounds.d.mts +3 -0
- package/runtime/scripts/hfs/architecture/contract-bounds.mjs +72 -0
- package/runtime/scripts/hfs/architecture/index.mjs +1 -1
- package/runtime/scripts/hfs/architecture/tiers.d.mts +2 -1
- package/runtime/scripts/hfs/architecture/tiers.mjs +45 -4
- package/runtime/scripts/hfs/ci-declaration.d.mts +47 -1
- package/runtime/scripts/hfs/ci-declaration.mjs +115 -3
- package/runtime/scripts/hfs/repo-identity.d.mts +5 -1
- package/runtime/scripts/hfs/repo-identity.mjs +15 -10
- package/runtime/scripts/hfs/rules/ci-custody.mjs +21 -32
- package/runtime/scripts/hfs/rules/contract.d.mts +11 -3
- package/runtime/scripts/hfs/rules/contract.mjs +50 -11
- package/runtime/scripts/hfs/rules/env-parity.mjs +15 -6
- package/runtime/scripts/hfs/secret-standard.d.mts +1 -0
- package/runtime/scripts/hfs/secret-standard.mjs +25 -4
- package/runtime/scripts/hfs/secret-store.d.mts +5 -0
- package/runtime/scripts/hfs/secret-store.mjs +31 -0
- package/src/main.d.mts +3 -1
- package/src/main.mjs +14 -11
- package/sync/ci-commands.mjs +2 -1
- package/sync/quality-services.mjs +3 -2
- package/sync/readme-badges.mjs +1 -1
- package/sync/sonar-key.d.mts +2 -3
- package/sync/sonar-key.mjs +7 -14
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +7 -3
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.0.6
|
|
4
|
+
|
|
5
|
+
- Changed: Support sealed per-project quality-service credentials and preserve valid composite cache entries and reused spec records.
|
|
6
|
+
|
|
7
|
+
- Fixed: Preserve generated-contract provenance in front-end tier and bounds checks.
|
|
8
|
+
|
|
9
|
+
## 5.0.5
|
|
10
|
+
|
|
11
|
+
- Changed: Govern each app-check linter, compiler, test discovery and coverage, contract emitter and boot child through the CLI execution port with actual worker limits and complete output capture. Preserve cache identities, closure, flush behavior and coverage thresholds. Record source-proven native Page body layouts with their contextual constraints.
|
|
12
|
+
|
|
3
13
|
## 5.0.4
|
|
4
14
|
|
|
5
15
|
- Changed: Record the existing shared Page semantic role in the selected canonical Grammar component contract, so universal composition proof does not infer concrete API names or accept an app-controlled switch.
|
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ starci app slot <path> [--cwd <dir>] [--json]
|
|
|
26
26
|
starci app emit [--cwd <dir>]
|
|
27
27
|
starci app db drift [--db-url-env <NAME>] [--json] [--cwd <dir>]
|
|
28
28
|
starci app secret init | <set|get|unset|rename|remove|materialize|list|verify|recipients|migrate|split|audit|server-key> --env <env> ... [--cwd <dir>]
|
|
29
|
+
starci app ci secret set <NAME> [--from-custody] [--env <env>] [--repo <owner/name>] [--execute]
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
`starci app add` writes every front-end unit, in either edition, already in the shape of the harness's `contexts/code/fe`
|
|
@@ -227,8 +228,9 @@ read until `migrate` moves it (`runtime/scripts/lib/sealed-member.mjs` names bot
|
|
|
227
228
|
seals every named key in one sops write (`runtime/scripts/hfs/secret.mjs`): on a terminal it prompts `<KEY>: ` for each with
|
|
228
229
|
masked input (one `*` per character; a paste, backspace, Enter, Ctrl+C to cancel with nothing written), redirected stdin gives
|
|
229
230
|
one key its whole text or several one `KEY=value` line each, `--from-file KEY=path` reads a key byte for byte
|
|
230
|
-
(`secret-prompt.mjs`), and `--generate <bytes>` makes a random value per key.
|
|
231
|
-
|
|
231
|
+
(`secret-prompt.mjs`), and `--generate <bytes>` makes a random value per key. Keys declared by exact service credential
|
|
232
|
+
references stay with that consumer; other keys are declared in the environment's `secrets:` of a compose environment
|
|
233
|
+
(`declareKeys`, `secret-standard.mjs`), while a swarm environment's are named to wire by hand.
|
|
232
234
|
A member is sealed to its environment's rule of the managed `.sops.yaml`; `recipients --env <env> [--add|--remove <age1...|owner>]`
|
|
233
235
|
rewrites that rule (`environmentRecipients`, `secret-init.mjs`), and `init` takes the owner's identity as the app's recipient.
|
|
234
236
|
Re-sealing a member whose every key the set replaces needs no age identity; adding a key to one does, and the identity is found
|
|
@@ -244,6 +246,24 @@ except `get --reveal`.
|
|
|
244
246
|
|
|
245
247
|
## Sonar
|
|
246
248
|
|
|
249
|
+
The product's `.starcistacks/application-stacks.yaml` holds its connection profile: provider, local/hosted mode, public
|
|
250
|
+
URL and `projects` entries. Each stored username, password, private URL or token has a separate `credentials[]` reference
|
|
251
|
+
to its actual sealed member and `key`; a private endpoint uses `host.fromCredential`. A project analysis reference has
|
|
252
|
+
`project: <projects[].key>`, independently of its reference `id`. Operator/admin login keys live in a separate authority
|
|
253
|
+
member and are never selected implicitly by analysis or CI. Change the declared connection profile when moving to a
|
|
254
|
+
hosted/cloud server; usernames, passwords and tokens remain sealed.
|
|
255
|
+
|
|
256
|
+
The CI execution copy is explicit: `services.sonar.ci.secrets: [{name: <CI_NAME>, credential: <analysis-id>}]`, with
|
|
257
|
+
`ci.token: <CI_NAME>` and the name/scope declared in the top-level `ci.secrets`. Declare the exact product credential
|
|
258
|
+
first, seal it with `starci app secret set --env <declared-env> <provider> <KEY>`, then run
|
|
259
|
+
`starci app ci secret set <CI_NAME> --from-custody` to inspect the plan and add `--execute` to stream its selected value
|
|
260
|
+
to the CI provider. A dry run decrypts no credential, reads no stdin and reaches no remote. Custody input uses terminal or
|
|
261
|
+
closed/null stdin; a competing pipe/file channel, even empty, is refused without reading it. A private endpoint has its
|
|
262
|
+
own explicit secret-copy link selected by `ci.hostVar`; public endpoint selectors read CI variables. The native local release opens
|
|
263
|
+
the selected product token through the common SOPS identity loader in memory and passes it only to the scanner child.
|
|
264
|
+
Missing, ambiguous, unopenable, wrong-project or runtime-granted custody is a failure, with no host/global fallback.
|
|
265
|
+
Analysis never creates a project, replaces its history or rotates a token. OIDC Codecov keeps no synthetic token member.
|
|
266
|
+
|
|
247
267
|
One mechanism for both sides: every finding of the canon is imported into Sonar, and the quality gate
|
|
248
268
|
fails while any is open. Nothing is configured per app; the pieces are managed files (`starci app sync`) and this package.
|
|
249
269
|
|
package/cache/app-families.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import { type CheckFinding } from '../runtime/scripts/hfs/check.mjs';
|
|
2
3
|
import { type TestFamilyResult } from './test-family.mjs';
|
|
3
4
|
import { type TypesFamilyResult, type TypesProgram } from './types-family.mjs';
|
|
@@ -6,24 +7,26 @@ import type { CheckSession } from './types.mjs';
|
|
|
6
7
|
/** The tsc programs of the app: the back end and each front-end workspace, from the tracked tsconfigs. */
|
|
7
8
|
export declare function programsOf(tracked: readonly string[]): TypesProgram[];
|
|
8
9
|
/** `--types`: `tsc --noEmit` per workspace program through the app's TypeScript, reused by program key. */
|
|
9
|
-
export declare function runTypesForApp(session: CheckSession, { repoRoot, tracked, env }: {
|
|
10
|
+
export declare function runTypesForApp(session: CheckSession, { repoRoot, tracked, env, execute }: {
|
|
10
11
|
repoRoot: string;
|
|
11
12
|
tracked: readonly string[];
|
|
12
13
|
env: NodeJS.ProcessEnv;
|
|
13
|
-
|
|
14
|
+
execute?: HeavyNodeRun | undefined;
|
|
15
|
+
}): Promise<{
|
|
14
16
|
result: TypesFamilyResult;
|
|
15
17
|
findings: CheckFinding[];
|
|
16
|
-
}
|
|
18
|
+
}>;
|
|
17
19
|
/** `--test`: the unit specs of the back end through the app's own Jest, reused by closure key; the merged total is judged by `@starci/hfs` (the coverage proof). */
|
|
18
|
-
export declare function runTestForApp(session: CheckSession, { repoRoot, tracked }: {
|
|
20
|
+
export declare function runTestForApp(session: CheckSession, { repoRoot, tracked, execute }: {
|
|
19
21
|
repoRoot: string;
|
|
20
22
|
tracked: readonly string[];
|
|
23
|
+
execute?: HeavyNodeRun | undefined;
|
|
21
24
|
}): Promise<{
|
|
22
25
|
result: TestFamilyResult | null;
|
|
23
26
|
findings: CheckFinding[];
|
|
24
27
|
}>;
|
|
25
28
|
/** `--test --full --shard i/n`: one shard of the full unit run in the shape the shard merge reads; nothing is recorded. */
|
|
26
|
-
export declare function runTestShard(repoRoot: string, index: number, total: number): number
|
|
29
|
+
export declare function runTestShard(repoRoot: string, index: number, total: number, execute?: HeavyNodeRun): Promise<number>;
|
|
27
30
|
/** `--sonar`: the Sonar and Codecov baselines and the predictions from the merged coverage records; they never pass or fail a run. */
|
|
28
31
|
export declare function runSonarForApp(session: CheckSession, { repoRoot }: {
|
|
29
32
|
repoRoot: string;
|
package/cache/app-families.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
|
-
import {
|
|
8
|
+
import { completedNode, runHeavyNode } from '../runtime/scripts/api/node/run-node.mjs';
|
|
9
9
|
import { skillRoot } from '../runtime/engine/runtime-root.mjs';
|
|
10
10
|
import { coverageFrom } from '../runtime/scripts/hfs/coverage-from.mjs';
|
|
11
11
|
import { readWhy } from '../runtime/scripts/hfs/check.mjs';
|
|
@@ -30,11 +30,11 @@ export function programsOf(tracked) {
|
|
|
30
30
|
return programs.sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
31
31
|
}
|
|
32
32
|
/** `--types`: `tsc --noEmit` per workspace program through the app's TypeScript, reused by program key. */
|
|
33
|
-
export function runTypesForApp(session, { repoRoot, tracked, env }) {
|
|
33
|
+
export async function runTypesForApp(session, { repoRoot, tracked, env, execute }) {
|
|
34
34
|
const programs = programsOf(tracked);
|
|
35
35
|
const trackedSet = new Set(tracked);
|
|
36
|
-
const result = runTypesFamily({
|
|
37
|
-
session, programs, run: createTscRunner({ appRoot: repoRoot }), toolCacheDir: toolCacheDir(repoRoot, env),
|
|
36
|
+
const result = await runTypesFamily({
|
|
37
|
+
session, programs, run: createTscRunner({ appRoot: repoRoot, execute }), toolCacheDir: toolCacheDir(repoRoot, env),
|
|
38
38
|
inputsOf: (program) => {
|
|
39
39
|
const side = program.name.startsWith('be') ? 'be' : 'fe';
|
|
40
40
|
const prefix = `${program.reads?.[0] ?? side}/`;
|
|
@@ -45,12 +45,12 @@ export function runTypesForApp(session, { repoRoot, tracked, env }) {
|
|
|
45
45
|
return { result, findings: result.findings.map((found) => asFinding(found, 'TS_DIAGNOSTIC', 'error', 'tsc')) };
|
|
46
46
|
}
|
|
47
47
|
/** `--test`: the unit specs of the back end through the app's own Jest, reused by closure key; the merged total is judged by `@starci/hfs` (the coverage proof). */
|
|
48
|
-
export async function runTestForApp(session, { repoRoot, tracked }) {
|
|
48
|
+
export async function runTestForApp(session, { repoRoot, tracked, execute }) {
|
|
49
49
|
if (!tracked.includes('be/package.json') && !tracked.some((file) => file.startsWith('be/')))
|
|
50
50
|
return { result: null, findings: [] };
|
|
51
|
-
const jest = await createJestEnvironment({ appRoot: repoRoot, side: 'be' });
|
|
51
|
+
const jest = await createJestEnvironment({ appRoot: repoRoot, side: 'be', execute });
|
|
52
52
|
let judged = [];
|
|
53
|
-
const result = runTestFamily({
|
|
53
|
+
const result = await runTestFamily({
|
|
54
54
|
session, jest, side: 'be', merge: installedSpecMerge(repoRoot),
|
|
55
55
|
judge: (lcovPath) => {
|
|
56
56
|
const report = coverageFrom({ repoRoot, lcov: fs.readFileSync(lcovPath, 'utf8'), files: [...tracked] });
|
|
@@ -64,10 +64,12 @@ export async function runTestForApp(session, { repoRoot, tracked }) {
|
|
|
64
64
|
return { result, findings };
|
|
65
65
|
}
|
|
66
66
|
/** `--test --full --shard i/n`: one shard of the full unit run in the shape the shard merge reads; nothing is recorded. */
|
|
67
|
-
export function runTestShard(repoRoot, index, total) {
|
|
67
|
+
export async function runTestShard(repoRoot, index, total, execute) {
|
|
68
68
|
const shard = shardArgs(index, total);
|
|
69
69
|
const jestBin = createRequire(path.join(repoRoot, 'be', 'package.json')).resolve('jest/bin/jest');
|
|
70
|
-
const run =
|
|
70
|
+
const run = completedNode(await runHeavyNode(execute, { kind: 'test', label: `app check jest shard ${index}/${total}`, args: [jestBin, ...shard.args], cwd: path.join(repoRoot, 'be'), env: { ...process.env, ...shard.env }, maxBuffer: 256 * 1024 * 1024 }), 'jest shard');
|
|
71
|
+
process.stdout.write(run.stdout);
|
|
72
|
+
process.stderr.write(run.stderr);
|
|
71
73
|
return run.status ?? 1;
|
|
72
74
|
}
|
|
73
75
|
/** `--sonar`: the Sonar and Codecov baselines and the predictions from the merged coverage records; they never pass or fail a run. */
|
package/cache/hfs-family.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import { type MachineFn, type RepositoryResult } from '../runtime/scripts/hfs/check.mjs';
|
|
2
3
|
import type { Presets } from '../sync/index.mjs';
|
|
3
4
|
import type { CheckSession, FamilyReport } from './types.mjs';
|
|
@@ -20,6 +21,7 @@ export interface PassResult {
|
|
|
20
21
|
}
|
|
21
22
|
export interface PassInput {
|
|
22
23
|
readonly repoRoot: string;
|
|
24
|
+
readonly execute?: HeavyNodeRun | undefined;
|
|
23
25
|
readonly harness?: {
|
|
24
26
|
version?: string;
|
|
25
27
|
} | undefined;
|
package/cache/hfs-family.mjs
CHANGED
|
@@ -7,9 +7,9 @@ import { checkRepo, checkRepository, trackedFiles } from '../runtime/scripts/hfs
|
|
|
7
7
|
import { checkArchitecture } from '../runtime/scripts/hfs/architecture/index.mjs';
|
|
8
8
|
import { withoutBootProof } from '../runtime/scripts/hfs/rules/boot-proof.mjs';
|
|
9
9
|
import { withUnusedSymbolMemo } from '../runtime/scripts/hfs/rules/unused-symbol.mjs';
|
|
10
|
-
import {
|
|
10
|
+
import { contractEmitFindingsAsync } from '../runtime/scripts/hfs/rules/contract.mjs';
|
|
11
11
|
import { loadSlotManifest, readRepoDeclaration } from '../runtime/scripts/hfs/slots.mjs';
|
|
12
|
-
import {
|
|
12
|
+
import { emitContractsAsync } from '../emit/contracts.mjs';
|
|
13
13
|
import { summarize } from '../runtime/scripts/hfs/check-results.mjs';
|
|
14
14
|
import { managedFindings } from '../sync/managed.mjs';
|
|
15
15
|
import { RecordSet, scopedStep } from './records.mjs';
|
|
@@ -61,9 +61,9 @@ export async function runStructurePass(session, input) {
|
|
|
61
61
|
const key = keys.get('be');
|
|
62
62
|
const emit = await scopedStep(session, records, {
|
|
63
63
|
path: 'be', step: 'contract-emit', key: key?.key ?? 'none', reuse, inputs: key?.inputs ?? [],
|
|
64
|
-
compute: () => {
|
|
64
|
+
compute: async () => {
|
|
65
65
|
const files = tracked.filter((file) => file.startsWith(bePrefix)).map((file) => file.slice(bePrefix.length));
|
|
66
|
-
const emitted =
|
|
66
|
+
const emitted = await contractEmitFindingsAsync({ repoRoot: path.join(repoRoot, 'be'), files, repo: be, emit: (request) => emitContractsAsync({ ...request, execute: input.execute }) });
|
|
67
67
|
return JSON.parse(JSON.stringify({ findings: emitted.findings, apps: emitted.apps }));
|
|
68
68
|
},
|
|
69
69
|
compact: (value) => (value.findings).map(compact),
|
package/cache/run.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import { type CheckFinding, type RepositoryResult } from '../runtime/scripts/hfs/check.mjs';
|
|
2
3
|
import type { Presets } from '../sync/index.mjs';
|
|
3
4
|
import type { Prettier } from '../sync/format.mjs';
|
|
@@ -6,6 +7,7 @@ import { type CheckSession, type FamilyId, type FamilyReport } from './types.mjs
|
|
|
6
7
|
export declare const DEFAULT_FAMILIES: readonly FamilyId[];
|
|
7
8
|
export interface AppRunInput {
|
|
8
9
|
readonly repoRoot: string;
|
|
10
|
+
readonly execute?: HeavyNodeRun | undefined;
|
|
9
11
|
readonly tracked: readonly string[];
|
|
10
12
|
readonly session: CheckSession;
|
|
11
13
|
readonly selected: ReadonlySet<FamilyId>;
|
package/cache/run.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// run.mts - one `starci app check` / `starci app lint` pass over the selected families, against one session. It owns the order of the families,
|
|
2
|
-
// the judging of their findings (edition and the Vietnamese why of every code) and the merge into the one result the verb prints and the gate
|
|
3
|
-
// judges, so the two verbs never hold two answers for one tree.
|
|
4
1
|
import { skillRoot } from '../runtime/engine/runtime-root.mjs';
|
|
5
2
|
import { readWhy } from '../runtime/scripts/hfs/check.mjs';
|
|
6
3
|
import { judgedFindings, summarize, withWhy } from '../runtime/scripts/hfs/check-results.mjs';
|
|
@@ -23,7 +20,7 @@ export async function runAppFamilies(input) {
|
|
|
23
20
|
const track = (report) => void (report && reports.push(report));
|
|
24
21
|
const structure = selected.has('hfs') || selected.has('secrets');
|
|
25
22
|
const pass = structure
|
|
26
|
-
? await runStructurePass(session, { repoRoot, harness: input.harness, presets: input.presets, tracked: input.tracked, hfs: selected.has('hfs'), secrets: selected.has('secrets') })
|
|
23
|
+
? await runStructurePass(session, { repoRoot, harness: input.harness, presets: input.presets, tracked: input.tracked, hfs: selected.has('hfs'), secrets: selected.has('secrets'), execute: input.execute })
|
|
27
24
|
: null;
|
|
28
25
|
if (pass) {
|
|
29
26
|
track(pass.report);
|
|
@@ -43,7 +40,7 @@ export async function runAppFamilies(input) {
|
|
|
43
40
|
}
|
|
44
41
|
let wiring;
|
|
45
42
|
if (selected.has('wiring')) {
|
|
46
|
-
const proved = await runWiringFamily(session, { repoRoot, edition: input.edition, files: input.tracked });
|
|
43
|
+
const proved = await runWiringFamily(session, { repoRoot, edition: input.edition, files: input.tracked, execute: input.execute });
|
|
47
44
|
track(proved.report);
|
|
48
45
|
wiring = { status: proved.wiring.status, apps: proved.wiring.apps, findings: proved.wiring.findings };
|
|
49
46
|
extra.push(...proved.wiring.findings);
|
package/cache/session.mjs
CHANGED
|
@@ -137,7 +137,7 @@ export function createDiskSession(options) {
|
|
|
137
137
|
// A file edited while the run read it gets no record.
|
|
138
138
|
const settled = new Map();
|
|
139
139
|
for (const [folder, { records }] of replaced) {
|
|
140
|
-
settled.set(folder, records.filter((record) => index.hashOf(record.path) === undefined || hashFile(path.join(root, record.path)) === record.
|
|
140
|
+
settled.set(folder, records.filter((record) => index.hashOf(record.path) === undefined || hashFile(path.join(root, record.path)) === index.hashOf(record.path)));
|
|
141
141
|
}
|
|
142
142
|
if (trust.kind === 'base') {
|
|
143
143
|
const committed = new Map();
|
package/cache/test-family.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import type { CacheRecord, CheckSession, FamilyReport, JsonValue } from './types.mjs';
|
|
2
3
|
/** The folder of the session that holds the records of this family. */
|
|
3
4
|
export declare const TEST_FOLDER = "test-coverage";
|
|
@@ -33,7 +34,7 @@ export interface JestEnvironment {
|
|
|
33
34
|
/** The digest of the unit project's configuration, free of host paths. */
|
|
34
35
|
readonly configHash: string;
|
|
35
36
|
/** Runs `specs` (all of them when `specs` is every spec) writing the per-spec coverage into `coverageDir`. */
|
|
36
|
-
run(specs: readonly string[], coverageDir: string): RunOutcome
|
|
37
|
+
run(specs: readonly string[], coverageDir: string): RunOutcome | Promise<RunOutcome>;
|
|
37
38
|
/** Reads what the reporter wrote into `coverageDir`. */
|
|
38
39
|
readCoverage(coverageDir: string): readonly SpecCoverageOutput[];
|
|
39
40
|
}
|
|
@@ -67,7 +68,7 @@ export interface TestFamilyResult {
|
|
|
67
68
|
readonly ok: boolean;
|
|
68
69
|
readonly ran: readonly string[];
|
|
69
70
|
readonly reused: readonly string[];
|
|
70
|
-
/** Specs that failed
|
|
71
|
+
/** Specs that failed, did not report, or whose inputs changed: nothing of theirs is stored. */
|
|
71
72
|
readonly failed: readonly string[];
|
|
72
73
|
/** The merged lcov, or undefined when no spec has a record. */
|
|
73
74
|
readonly lcov: string | undefined;
|
|
@@ -94,7 +95,7 @@ export declare function sourcesOf(record: CacheRecord): Record<string, SourceIte
|
|
|
94
95
|
* Runs the unit specs against the cache. Diff mode runs the specs with no current record, full mode all of them; a passing spec's coverage is stored under its key,
|
|
95
96
|
* a failing one's is not, and the folder is replaced with exactly the current specs' records. The merged total is written to `<side>/coverage/lcov.info` and judged.
|
|
96
97
|
*/
|
|
97
|
-
export declare function runTestFamily(options: TestFamilyOptions): TestFamilyResult
|
|
98
|
+
export declare function runTestFamily(options: TestFamilyOptions): Promise<TestFamilyResult>;
|
|
98
99
|
/** The part of a Jest project config the closure needs. */
|
|
99
100
|
interface ProjectConfigLike {
|
|
100
101
|
readonly rootDir: string;
|
|
@@ -111,11 +112,12 @@ export declare function projectConfigHash(config: ProjectConfigLike): string;
|
|
|
111
112
|
* The app's own Jest, resolved from `jestFrom` (a package.json or folder inside the app's install): its unit project's configuration, its haste map and resolver
|
|
112
113
|
* (so `moduleNameMapper`, `jest.mock` targets and setup files count as Jest sees them), and its binary to run specs. `side` is the Jest root, app-relative.
|
|
113
114
|
*/
|
|
114
|
-
export declare function createJestEnvironment({ appRoot, side, jestFrom, project }: {
|
|
115
|
+
export declare function createJestEnvironment({ appRoot, side, jestFrom, project, execute }: {
|
|
115
116
|
appRoot: string;
|
|
116
117
|
side: string;
|
|
117
118
|
jestFrom?: string;
|
|
118
119
|
project?: string;
|
|
120
|
+
execute?: HeavyNodeRun | undefined;
|
|
119
121
|
}): Promise<JestEnvironment>;
|
|
120
122
|
/** The installed preset's union merge (resolved like the shard merge of `app coverage`): the records in, the lcov and coverage-final.json out. */
|
|
121
123
|
export declare function installedSpecMerge(appRoot: string, from?: string): MergeSpecRecords;
|
package/cache/test-family.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import fs from 'node:fs';
|
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
|
-
import {
|
|
11
|
+
import { completedNode, runHeavyNode } from '../runtime/scripts/api/node/run-node.mjs';
|
|
12
12
|
import { combineHashes, hashFile } from './hash.mjs';
|
|
13
13
|
import { canonicalJson } from './jsonl.mjs';
|
|
14
14
|
/** The folder of the session that holds the records of this family. */
|
|
@@ -57,7 +57,7 @@ function recordOf(spec, key, sources, side) {
|
|
|
57
57
|
* Runs the unit specs against the cache. Diff mode runs the specs with no current record, full mode all of them; a passing spec's coverage is stored under its key,
|
|
58
58
|
* a failing one's is not, and the folder is replaced with exactly the current specs' records. The merged total is written to `<side>/coverage/lcov.info` and judged.
|
|
59
59
|
*/
|
|
60
|
-
export function runTestFamily(options) {
|
|
60
|
+
export async function runTestFamily(options) {
|
|
61
61
|
const { session, jest, side, merge, judge } = options;
|
|
62
62
|
const decision = session.decide('test');
|
|
63
63
|
const hashOf = (file) => session.index.hashOf(file) ?? hashFile(path.join(session.root, file));
|
|
@@ -79,13 +79,12 @@ export function runTestFamily(options) {
|
|
|
79
79
|
try {
|
|
80
80
|
fs.rmSync(scratch, { recursive: true, force: true });
|
|
81
81
|
fs.mkdirSync(scratch, { recursive: true });
|
|
82
|
-
output = jest.run(toRun, scratch).output;
|
|
82
|
+
output = (await jest.run(toRun, scratch)).output;
|
|
83
83
|
const reported = new Map(jest.readCoverage(scratch).map((file) => [file.spec, file]));
|
|
84
84
|
for (const spec of toRun) {
|
|
85
85
|
const file = reported.get(posix(path.relative(path.join(session.root, side), path.join(session.root, spec))));
|
|
86
|
-
const key =
|
|
87
|
-
|
|
88
|
-
if (file?.passed === true && key === keys.get(spec))
|
|
86
|
+
const key = keys.get(spec);
|
|
87
|
+
if (file?.passed === true && key !== undefined)
|
|
89
88
|
fresh.push(recordOf(spec, key, file.sources, side));
|
|
90
89
|
else
|
|
91
90
|
failed.push(spec);
|
|
@@ -96,9 +95,18 @@ export function runTestFamily(options) {
|
|
|
96
95
|
fs.rmSync(scratch, { recursive: true, force: true });
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
|
-
|
|
98
|
+
// Recheck every candidate against disk, including a reused spec whose inputs another running spec could change.
|
|
99
|
+
const records = [...kept, ...fresh].filter((record) => {
|
|
100
|
+
const key = specKey({ spec: record.path, closure: jest.closureOf(record.path), configHash: jest.configHash, hashOf: (file) => hashFile(path.join(session.root, file)) });
|
|
101
|
+
if (key === keys.get(record.path))
|
|
102
|
+
return true;
|
|
103
|
+
failed.push(record.path);
|
|
104
|
+
return false;
|
|
105
|
+
});
|
|
106
|
+
const settled = new Set(records.map((record) => record.path));
|
|
107
|
+
const reused = kept.filter((record) => settled.has(record.path));
|
|
108
|
+
progress.tick(jest.specs.length, reused.length, toRun.length);
|
|
100
109
|
progress.end();
|
|
101
|
-
const records = [...kept, ...fresh];
|
|
102
110
|
session.replace(TEST_FOLDER, records);
|
|
103
111
|
let lcov;
|
|
104
112
|
let threshold;
|
|
@@ -117,13 +125,13 @@ export function runTestFamily(options) {
|
|
|
117
125
|
mode: decision.mode,
|
|
118
126
|
reason: decision.reason,
|
|
119
127
|
checked: toRun.length,
|
|
120
|
-
reused:
|
|
128
|
+
reused: reused.length,
|
|
121
129
|
newFindings: failed.length + thresholdFindings,
|
|
122
130
|
knownFindings: 0,
|
|
123
131
|
fixedFindings: 0,
|
|
124
132
|
};
|
|
125
133
|
session.report(report);
|
|
126
|
-
return { ok: failed.length === 0 && (threshold?.ok ?? records.length === 0), ran: toRun, reused:
|
|
134
|
+
return { ok: failed.length === 0 && (threshold?.ok ?? records.length === 0), ran: toRun, reused: reused.map((record) => record.path), failed, lcov, threshold, report, output };
|
|
127
135
|
}
|
|
128
136
|
/** The digest of a project config with host paths turned into `<root>` and the keys that differ per host dropped. */
|
|
129
137
|
export function projectConfigHash(config) {
|
|
@@ -144,7 +152,7 @@ export function projectConfigHash(config) {
|
|
|
144
152
|
* The app's own Jest, resolved from `jestFrom` (a package.json or folder inside the app's install): its unit project's configuration, its haste map and resolver
|
|
145
153
|
* (so `moduleNameMapper`, `jest.mock` targets and setup files count as Jest sees them), and its binary to run specs. `side` is the Jest root, app-relative.
|
|
146
154
|
*/
|
|
147
|
-
export async function createJestEnvironment({ appRoot, side, jestFrom, project = 'unit' }) {
|
|
155
|
+
export async function createJestEnvironment({ appRoot, side, jestFrom, project = 'unit', execute }) {
|
|
148
156
|
const root = path.resolve(appRoot, side);
|
|
149
157
|
const fromApp = createRequire(jestFrom ?? path.join(root, 'package.json'));
|
|
150
158
|
const jestPackage = fromApp.resolve('jest/package.json');
|
|
@@ -189,7 +197,7 @@ export async function createJestEnvironment({ appRoot, side, jestFrom, project =
|
|
|
189
197
|
const relative = (file) => posix(path.relative(appRoot, file));
|
|
190
198
|
const setup = (name) => [...(config[name] ?? [])];
|
|
191
199
|
const setupClosure = closureFrom([...setup('setupFiles'), ...setup('setupFilesAfterEnv')]).map(relative);
|
|
192
|
-
const listed =
|
|
200
|
+
const listed = completedNode(await runHeavyNode(execute, { kind: 'test', label: `app check jest ${project} discovery`, args: [jestBin, '--selectProjects', project, '--listTests'], cwd: root, maxBuffer: 64 * 1024 * 1024 }), 'jest discovery');
|
|
193
201
|
if (listed.status !== 0)
|
|
194
202
|
throw new Error(`jest --listTests failed under ${side}: ${(listed.stderr || listed.stdout).split('\n')[0]}`);
|
|
195
203
|
const specs = listed.stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => path.isAbsolute(line)).map((line) => relative(path.resolve(line))).sort();
|
|
@@ -206,7 +214,7 @@ export async function createJestEnvironment({ appRoot, side, jestFrom, project =
|
|
|
206
214
|
closures.set(spec, closure);
|
|
207
215
|
return closure;
|
|
208
216
|
},
|
|
209
|
-
run(toRun, coverageDir) {
|
|
217
|
+
async run(toRun, coverageDir) {
|
|
210
218
|
const base = [jestBin, '--selectProjects', project, '--coverage', '--ci'];
|
|
211
219
|
const everything = toRun.length === specs.length;
|
|
212
220
|
const sideRelative = toRun.map((spec) => posix(path.relative(root, path.resolve(appRoot, spec))));
|
|
@@ -223,7 +231,7 @@ export async function createJestEnvironment({ appRoot, side, jestFrom, project =
|
|
|
223
231
|
let output = '';
|
|
224
232
|
for (const chunk of chunks) {
|
|
225
233
|
const args = chunk.length > 0 ? [...base, '--runTestsByPath', ...chunk] : base;
|
|
226
|
-
const result =
|
|
234
|
+
const result = completedNode(await runHeavyNode(execute, { kind: 'test', label: `app check jest ${project} coverage`, args, cwd: root, env: { ...process.env, STARCI_SPEC_COVERAGE_DIR: coverageDir }, maxBuffer: 256 * 1024 * 1024 }), 'jest coverage');
|
|
227
235
|
status = status || (result.status ?? 1);
|
|
228
236
|
output += `${result.stdout}${result.stderr}`;
|
|
229
237
|
}
|
package/cache/types-family.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import type { CheckSession, FamilyReport, Finding } from './types.mjs';
|
|
2
3
|
/** The folder of the session that holds the records of this family. */
|
|
3
4
|
export declare const TYPES_FOLDER = "check/types";
|
|
@@ -29,7 +30,7 @@ export interface TypesFamilyOptions {
|
|
|
29
30
|
readonly session: CheckSession;
|
|
30
31
|
readonly programs: readonly TypesProgram[];
|
|
31
32
|
/** Runs one program's `tsc`; `buildInfo` is where its `.tsbuildinfo` goes (the untracked tool cache). */
|
|
32
|
-
readonly run: (program: TypesProgram, buildInfo: string) => TscOutcome
|
|
33
|
+
readonly run: (program: TypesProgram, buildInfo: string) => TscOutcome | Promise<TscOutcome>;
|
|
33
34
|
/** The untracked tool cache of slot `app.tool-cache` (`<STARCI_CACHE_HOME>/<repo>/app-check`). */
|
|
34
35
|
readonly toolCacheDir: string;
|
|
35
36
|
readonly inputsOf?: (program: TypesProgram) => ProgramInputs;
|
|
@@ -63,12 +64,13 @@ export declare function parseTscOutput(output: string, appRoot: string, fallback
|
|
|
63
64
|
* Runs the type check of every program against the cache. A program whose key is unchanged takes its recorded diagnostics; a changed one runs in full (all of them
|
|
64
65
|
* under `--full`). A run that produced no diagnostics and failed (tsc itself could not complete) stores nothing, and so does a program whose files changed while it ran.
|
|
65
66
|
*/
|
|
66
|
-
export declare function runTypesFamily(options: TypesFamilyOptions): TypesFamilyResult
|
|
67
|
+
export declare function runTypesFamily(options: TypesFamilyOptions): Promise<TypesFamilyResult>;
|
|
67
68
|
/**
|
|
68
69
|
* The real `tsc` of an app: the program's own TypeScript (resolved from its folder, so the version the lockfile pins), `--noEmit` with an incremental build info in the
|
|
69
70
|
* tool cache, run from the app root so paths in the output are app-relative.
|
|
70
71
|
*/
|
|
71
|
-
export declare function createTscRunner({ appRoot, typescriptFrom }: {
|
|
72
|
+
export declare function createTscRunner({ appRoot, typescriptFrom, execute }: {
|
|
72
73
|
appRoot: string;
|
|
73
74
|
typescriptFrom?: string;
|
|
75
|
+
execute?: HeavyNodeRun | undefined;
|
|
74
76
|
}): TypesFamilyOptions['run'];
|
package/cache/types-family.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { createRequire } from 'node:module';
|
|
9
|
-
import {
|
|
9
|
+
import { completedNode, runHeavyNode } from '../runtime/scripts/api/node/run-node.mjs';
|
|
10
10
|
import { CACHE_DIR } from './types.mjs';
|
|
11
11
|
import { combineHashes, hashFile } from './hash.mjs';
|
|
12
12
|
import { sortFindings } from './jsonl.mjs';
|
|
@@ -84,7 +84,7 @@ const identity = (finding) => `${finding.path}\0${finding.rule}\0${finding.messa
|
|
|
84
84
|
* Runs the type check of every program against the cache. A program whose key is unchanged takes its recorded diagnostics; a changed one runs in full (all of them
|
|
85
85
|
* under `--full`). A run that produced no diagnostics and failed (tsc itself could not complete) stores nothing, and so does a program whose files changed while it ran.
|
|
86
86
|
*/
|
|
87
|
-
export function runTypesFamily(options) {
|
|
87
|
+
export async function runTypesFamily(options) {
|
|
88
88
|
const { session, programs, run, toolCacheDir } = options;
|
|
89
89
|
const decision = session.decide('types');
|
|
90
90
|
const previous = new Map(session.records(TYPES_FOLDER).filter((record) => record.step === TSC_STEP).map((record) => [record.path, record]));
|
|
@@ -108,7 +108,7 @@ export function runTypesFamily(options) {
|
|
|
108
108
|
reused.push(program.name);
|
|
109
109
|
}
|
|
110
110
|
else {
|
|
111
|
-
const outcome = run(program, tsBuildInfoPath(toolCacheDir, session.root, program));
|
|
111
|
+
const outcome = await run(program, tsBuildInfoPath(toolCacheDir, session.root, program));
|
|
112
112
|
ran.push(program.name);
|
|
113
113
|
current = parseTscOutput(outcome.output, session.root, program.tsconfig);
|
|
114
114
|
if (current.length === 0 && outcome.status !== 0)
|
|
@@ -141,14 +141,15 @@ export function runTypesFamily(options) {
|
|
|
141
141
|
* The real `tsc` of an app: the program's own TypeScript (resolved from its folder, so the version the lockfile pins), `--noEmit` with an incremental build info in the
|
|
142
142
|
* tool cache, run from the app root so paths in the output are app-relative.
|
|
143
143
|
*/
|
|
144
|
-
export function createTscRunner({ appRoot, typescriptFrom }) {
|
|
145
|
-
return (program, buildInfo) => {
|
|
144
|
+
export function createTscRunner({ appRoot, typescriptFrom, execute }) {
|
|
145
|
+
return async (program, buildInfo) => {
|
|
146
146
|
const folder = path.resolve(appRoot, path.posix.dirname(program.tsconfig));
|
|
147
147
|
const tsc = path.join(path.dirname(createRequire(typescriptFrom ?? path.join(folder, 'package.json')).resolve('typescript/package.json')), 'bin', 'tsc');
|
|
148
148
|
fs.mkdirSync(path.dirname(buildInfo), { recursive: true });
|
|
149
|
-
const result =
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const result = completedNode(await runHeavyNode(execute, {
|
|
150
|
+
kind: 'tsc', label: `app check tsc ${program.name}`, args: [tsc, '-p', posix(path.relative(appRoot, path.resolve(appRoot, program.tsconfig))), '--noEmit', '--incremental', '--tsBuildInfoFile', buildInfo, '--pretty', 'false'],
|
|
151
|
+
cwd: appRoot, maxBuffer: 256 * 1024 * 1024,
|
|
152
|
+
}), 'tsc');
|
|
152
153
|
return { status: result.status ?? 1, output: `${result.stdout}${result.stderr}` };
|
|
153
154
|
};
|
|
154
155
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HeavyNodeRun } from '../runtime/scripts/api/node/run-node.mjs';
|
|
1
2
|
import { type WiringResult } from '../runtime/scripts/hfs/app-wiring.mjs';
|
|
2
3
|
import type { CheckSession, FamilyReport } from './types.mjs';
|
|
3
4
|
export declare const WIRING_FOLDER = "check/wiring";
|
|
@@ -6,9 +7,10 @@ export interface WiringFamilyResult {
|
|
|
6
7
|
readonly report: FamilyReport;
|
|
7
8
|
}
|
|
8
9
|
/** The boot proof family. `explicit` is the whole-tree proof of `--wiring`; without it a missing install leaves the compile unjudged, as the content finder does. */
|
|
9
|
-
export declare function runWiringFamily(session: CheckSession, { repoRoot, edition, files, run }: {
|
|
10
|
+
export declare function runWiringFamily(session: CheckSession, { repoRoot, edition, files, run, execute }: {
|
|
10
11
|
repoRoot: string;
|
|
11
12
|
edition?: string | undefined;
|
|
12
13
|
files?: readonly string[] | undefined;
|
|
13
|
-
run?: ((repoRoot: string) => WiringResult) | undefined;
|
|
14
|
+
run?: ((repoRoot: string) => WiringResult | Promise<WiringResult>) | undefined;
|
|
15
|
+
execute?: HeavyNodeRun | undefined;
|
|
14
16
|
}): Promise<WiringFamilyResult>;
|
package/cache/wiring-family.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// back-end side key is unchanged. A timeout reached no verdict and is never stored.
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
5
|
+
import { appWiringAsync, BOOT_PROOF_TIMEOUT } from '../runtime/scripts/hfs/app-wiring.mjs';
|
|
6
6
|
import { loadSlotManifest, readRepoDeclaration } from '../runtime/scripts/hfs/slots.mjs';
|
|
7
7
|
import { trackedFiles } from '../runtime/scripts/hfs/check.mjs';
|
|
8
8
|
import { RecordSet, scopedStep } from './records.mjs';
|
|
@@ -11,16 +11,16 @@ import { sideKey } from './side-key.mjs';
|
|
|
11
11
|
export const WIRING_FOLDER = 'check/wiring';
|
|
12
12
|
const compact = (finding) => ({ path: finding.path, line: 0, column: 0, rule: finding.code, message: finding.message });
|
|
13
13
|
/** The boot proof family. `explicit` is the whole-tree proof of `--wiring`; without it a missing install leaves the compile unjudged, as the content finder does. */
|
|
14
|
-
export async function runWiringFamily(session, { repoRoot, edition, files, run }) {
|
|
14
|
+
export async function runWiringFamily(session, { repoRoot, edition, files, run, execute }) {
|
|
15
15
|
const decision = session.decide('wiring');
|
|
16
16
|
const records = new RecordSet(session.records(WIRING_FOLDER));
|
|
17
17
|
const key = sideKey(repoRoot, session.index, 'be');
|
|
18
|
-
const prove = () => {
|
|
18
|
+
const prove = async () => {
|
|
19
19
|
if (run)
|
|
20
20
|
return run(repoRoot);
|
|
21
21
|
const declared = readRepoDeclaration(loadSlotManifest(), repoRoot);
|
|
22
22
|
const repo = { ...declared, edition: edition ?? declared.edition };
|
|
23
|
-
const proof =
|
|
23
|
+
const proof = await appWiringAsync({ repoRoot, repo: repo, files: [...(files ?? trackedFiles(repoRoot))], execute });
|
|
24
24
|
const installed = fs.existsSync(path.join(repoRoot, 'node_modules')) || fs.existsSync(path.join(repoRoot, 'be', 'node_modules'));
|
|
25
25
|
const findings = proof.findings.filter((finding) => installed || finding.reason !== 'module or proof could not be compiled');
|
|
26
26
|
return { ...proof, findings, ok: findings.length === 0 };
|
|
@@ -28,7 +28,7 @@ export async function runWiringFamily(session, { repoRoot, edition, files, run }
|
|
|
28
28
|
const step = session.progress.step('wiring', 'boot proof', Math.max(1, key.inputs.length));
|
|
29
29
|
const { value, reused } = await scopedStep(session, records, {
|
|
30
30
|
path: 'be', step: 'boot-proof', key: key.key, reuse: decision.mode === 'diff', inputs: key.inputs,
|
|
31
|
-
compute: () => JSON.parse(JSON.stringify(prove())),
|
|
31
|
+
compute: async () => JSON.parse(JSON.stringify(await prove())),
|
|
32
32
|
compact: (found) => found.findings.map(compact),
|
|
33
33
|
store: (found) => !found.findings.some((finding) => finding.code === BOOT_PROOF_TIMEOUT),
|
|
34
34
|
});
|
package/ci/cli.d.mts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type SecretSops } from '../runtime/scripts/hfs/secret-sops.mjs';
|
|
2
|
+
import type { AgeHost } from '../runtime/scripts/hfs/secret-init.mjs';
|
|
1
3
|
import type { GhSpawn, StdinSource } from './github.mjs';
|
|
2
4
|
/** The commands of `starci app ci` (besides `local`) with the options each takes; the parser refuses every other. */
|
|
3
5
|
export declare const COMMANDS: Record<string, string[]>;
|
|
4
6
|
export declare const BOOLEAN: Set<string>;
|
|
5
7
|
/** Manifest and sealed key reads remain offline; only github.mjs may invoke the GitHub CLI. */
|
|
6
|
-
export declare function ciMain(argv: string[], { cwd, stdout, stderr, stdin, env, tools }?: {
|
|
8
|
+
export declare function ciMain(argv: string[], { cwd, stdout, stderr, stdin, env, tools, hostIdentity, sops, platform, inputRedirected }?: {
|
|
7
9
|
cwd?: string | undefined;
|
|
8
10
|
stdout?: ((text: string) => unknown) | undefined;
|
|
9
11
|
stderr?: ((text: string) => unknown) | undefined;
|
|
@@ -12,4 +14,8 @@ export declare function ciMain(argv: string[], { cwd, stdout, stderr, stdin, env
|
|
|
12
14
|
tools?: {
|
|
13
15
|
spawn?: GhSpawn | undefined;
|
|
14
16
|
} | undefined;
|
|
17
|
+
hostIdentity?: AgeHost | null | undefined;
|
|
18
|
+
sops?: Pick<SecretSops, 'decrypt'> | null | undefined;
|
|
19
|
+
platform?: NodeJS.Platform | undefined;
|
|
20
|
+
inputRedirected?: (() => boolean) | undefined;
|
|
15
21
|
}): Promise<number>;
|