@kici-dev/shared 0.1.27 → 0.2.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/dist/agent-platform.d.ts +32 -0
- package/dist/agent-platform.js +27 -0
- package/dist/agent-platform.test.d.ts +2 -0
- package/dist/ci-env.d.ts +2 -0
- package/dist/ci-env.js +3 -0
- package/dist/cold-store/bucket.d.ts +1 -1
- package/dist/cold-store/cold-store.d.ts +24 -1
- package/dist/cold-store/cold-store.js +17 -5
- package/dist/cold-store/index.js +1 -1
- package/dist/db-admin.d.ts +150 -9
- package/dist/db-admin.js +160 -46
- package/dist/db.d.ts +34 -0
- package/dist/db.js +30 -1
- package/dist/diagnostics/bundle-archive.js +5 -5
- package/dist/env/allowlist.d.ts +5 -0
- package/dist/env/allowlist.js +6 -1
- package/dist/env/define-env.d.ts +19 -2
- package/dist/env/define-env.js +18 -3
- package/dist/env/logger-env.d.ts +11 -9
- package/dist/env/logger-env.js +25 -9
- package/dist/idempotency-files.d.ts +7 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -4
- package/dist/telemetry/init.d.ts +18 -7
- package/dist/telemetry/init.js +36 -11
- package/dist/tmp-dir.d.ts +14 -0
- package/dist/tmp-dir.js +13 -0
- package/dist/tmp-dir.test.d.ts +2 -0
- package/dist/tmp.d.ts +2 -0
- package/dist/tmp.js +3 -0
- package/package.json +26 -17
- package/sbom.spdx.json +1222 -1618
package/dist/db-admin.js
CHANGED
|
@@ -2,6 +2,7 @@ import "./rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
import { createPool } from "./db.js";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import pg from "pg";
|
|
5
|
+
import { HoldType, unknownContributorHoldReason } from "@kici-dev/engine";
|
|
5
6
|
//#region src/db-admin.ts
|
|
6
7
|
/**
|
|
7
8
|
* Admin/DB-operations helpers shared between `kici-admin` (orchestrator DB)
|
|
@@ -389,13 +390,19 @@ const ENV_POLICY_COLUMNS = /* @__PURE__ */ new Set([
|
|
|
389
390
|
"allow_local_execution"
|
|
390
391
|
]);
|
|
391
392
|
/**
|
|
392
|
-
* Upsert
|
|
393
|
+
* Upsert a context row keyed by (org_id, name). Returns the env id and
|
|
393
394
|
* whether the row was newly inserted. `branchRestrictions` / `requiredReviewers`
|
|
394
395
|
* are JSON-serialised server-side; pass them as plain arrays or objects.
|
|
396
|
+
*
|
|
397
|
+
* An omitted `holdExpirySeconds` is written as NULL rather than a literal
|
|
398
|
+
* window: the column carries no DDL default, so "never set" and "cleared" both
|
|
399
|
+
* land on NULL and resolve through the one `DEFAULT_HOLD_EXPIRY_SECONDS`
|
|
400
|
+
* fallback on read. Writing a literal here would give this path a second,
|
|
401
|
+
* longer default that no read-side code knows about.
|
|
395
402
|
*/
|
|
396
403
|
async function seedContextDirect(databaseUrl, opts) {
|
|
397
404
|
if (opts.waitTimerSeconds != null && opts.waitTimerSeconds < 0) throw new Error(`context: waitTimerSeconds must be >= 0 (got ${opts.waitTimerSeconds})`);
|
|
398
|
-
if (opts.holdExpirySeconds != null && opts.holdExpirySeconds <
|
|
405
|
+
if (opts.holdExpirySeconds != null && opts.holdExpirySeconds < 1) throw new Error(`context: holdExpirySeconds must be >= 1 (got ${opts.holdExpirySeconds})`);
|
|
399
406
|
const pool = new pg.Pool({
|
|
400
407
|
connectionString: databaseUrl,
|
|
401
408
|
max: 1
|
|
@@ -407,7 +414,7 @@ async function seedContextDirect(databaseUrl, opts) {
|
|
|
407
414
|
(org_id, name, type, enabled, branch_restrictions, required_reviewers,
|
|
408
415
|
wait_timer_seconds, hold_expiry_seconds, minimum_trust, glob_pattern)
|
|
409
416
|
VALUES ($1, $2, COALESCE($3, 'fixed'), COALESCE($4, true), $5::jsonb, $6::jsonb,
|
|
410
|
-
$7,
|
|
417
|
+
$7, $8, $9, $10)
|
|
411
418
|
ON CONFLICT (org_id, name) DO UPDATE SET
|
|
412
419
|
type = COALESCE(EXCLUDED.type, contexts.type),
|
|
413
420
|
enabled = EXCLUDED.enabled,
|
|
@@ -440,7 +447,7 @@ async function seedContextDirect(databaseUrl, opts) {
|
|
|
440
447
|
}
|
|
441
448
|
}
|
|
442
449
|
/**
|
|
443
|
-
* Delete
|
|
450
|
+
* Delete a context keyed by (org_id, name). Returns whether a row was
|
|
444
451
|
* removed. The `context_bindings`, `context_variables`, and
|
|
445
452
|
* `context_source_overrides` children all carry
|
|
446
453
|
* `FOREIGN KEY (context_id) REFERENCES contexts(id) ON DELETE CASCADE`,
|
|
@@ -499,7 +506,7 @@ async function seedContextBindingDirect(databaseUrl, opts) {
|
|
|
499
506
|
*/
|
|
500
507
|
async function setContextPolicyDirect(databaseUrl, opts) {
|
|
501
508
|
if (opts.waitTimerSeconds != null && opts.waitTimerSeconds < 0) throw new Error(`context: waitTimerSeconds must be >= 0 (got ${opts.waitTimerSeconds})`);
|
|
502
|
-
if (opts.holdExpirySeconds != null && opts.holdExpirySeconds <
|
|
509
|
+
if (opts.holdExpirySeconds != null && opts.holdExpirySeconds < 1) throw new Error(`context: holdExpirySeconds must be >= 1 (got ${opts.holdExpirySeconds})`);
|
|
503
510
|
const setClauses = [];
|
|
504
511
|
const params = [];
|
|
505
512
|
let idx = 1;
|
|
@@ -587,11 +594,16 @@ async function showContextDirect(databaseUrl, opts) {
|
|
|
587
594
|
}
|
|
588
595
|
}
|
|
589
596
|
/**
|
|
590
|
-
* Create (or update)
|
|
597
|
+
* Create (or update) a context template + its seed variables in one
|
|
591
598
|
* transaction. Templates are represented as contexts with `type='template'`
|
|
592
599
|
* by convention. Returns `{ envId, variablesSet }`.
|
|
600
|
+
*
|
|
601
|
+
* An omitted `holdExpirySeconds` is written as NULL rather than a literal
|
|
602
|
+
* window, for the same reason as `seedContextDirect`: the column has no DDL
|
|
603
|
+
* default and every read resolves NULL through `DEFAULT_HOLD_EXPIRY_SECONDS`.
|
|
593
604
|
*/
|
|
594
605
|
async function createContextTemplateDirect(databaseUrl, opts) {
|
|
606
|
+
if (opts.holdExpirySeconds != null && opts.holdExpirySeconds < 1) throw new Error(`context: holdExpirySeconds must be >= 1 (got ${opts.holdExpirySeconds})`);
|
|
595
607
|
const pool = new pg.Pool({
|
|
596
608
|
connectionString: databaseUrl,
|
|
597
609
|
max: 1
|
|
@@ -602,7 +614,7 @@ async function createContextTemplateDirect(databaseUrl, opts) {
|
|
|
602
614
|
const row = (await client.query(`INSERT INTO contexts
|
|
603
615
|
(org_id, name, type, enabled, branch_restrictions, required_reviewers,
|
|
604
616
|
wait_timer_seconds, hold_expiry_seconds, minimum_trust)
|
|
605
|
-
VALUES ($1, $2, COALESCE($3, 'template'), true, $4::jsonb, $5::jsonb, $6,
|
|
617
|
+
VALUES ($1, $2, COALESCE($3, 'template'), true, $4::jsonb, $5::jsonb, $6, $7, $8)
|
|
606
618
|
ON CONFLICT (org_id, name) DO UPDATE SET
|
|
607
619
|
type = COALESCE(EXCLUDED.type, contexts.type),
|
|
608
620
|
branch_restrictions = EXCLUDED.branch_restrictions,
|
|
@@ -800,6 +812,38 @@ async function listExecutionRunsDirect(databaseUrl, opts = {}) {
|
|
|
800
812
|
}
|
|
801
813
|
}
|
|
802
814
|
/**
|
|
815
|
+
* READ-ONLY: SELECT check_run_tracking rows for a commit. One row per
|
|
816
|
+
* `(provider, owner, repo, sha, check_name)`, ordered by check_name.
|
|
817
|
+
*
|
|
818
|
+
* Each column answers a different question. `check_run_id` is written once,
|
|
819
|
+
* when the check run is created in the `queued` state, so it answers "did we
|
|
820
|
+
* create it?". `terminal_sent_at` is stamped only after the provider accepts
|
|
821
|
+
* the terminal `completed` PATCH, so it answers "did we complete it?". Every
|
|
822
|
+
* write here is best-effort, so a null column is "no record", never proof of
|
|
823
|
+
* failure — see the per-field notes for what each one does and does not prove.
|
|
824
|
+
*/
|
|
825
|
+
async function listCheckRunTrackingDirect(databaseUrl, opts) {
|
|
826
|
+
const pool = createPool(databaseUrl);
|
|
827
|
+
try {
|
|
828
|
+
const clauses = ["sha = $1"];
|
|
829
|
+
const params = [opts.sha];
|
|
830
|
+
if (opts.checkName !== void 0) {
|
|
831
|
+
clauses.push(`check_name = $2`);
|
|
832
|
+
params.push(opts.checkName);
|
|
833
|
+
}
|
|
834
|
+
const limit = Math.max(1, Math.min(1e3, opts.limit ?? 50));
|
|
835
|
+
return { rows: (await pool.query(`SELECT provider, owner, repo, sha, check_name,
|
|
836
|
+
check_run_id::text AS check_run_id,
|
|
837
|
+
build_creation_state, run_id, in_progress_sent_at, terminal_sent_at
|
|
838
|
+
FROM check_run_tracking
|
|
839
|
+
WHERE ${clauses.join(" AND ")}
|
|
840
|
+
ORDER BY check_name
|
|
841
|
+
LIMIT ${limit}`, params)).rows };
|
|
842
|
+
} finally {
|
|
843
|
+
await pool.end();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
803
847
|
* READ-ONLY: fetch a single run by run_id AND its jobs. Throws if no run
|
|
804
848
|
* matches the run_id. Jobs list may be empty for pending runs.
|
|
805
849
|
*/
|
|
@@ -1648,6 +1692,8 @@ async function seedUniversalGitSourceDirect(databaseUrl, opts) {
|
|
|
1648
1692
|
await pool.end();
|
|
1649
1693
|
}
|
|
1650
1694
|
}
|
|
1695
|
+
/** repo_identifier used for the different-repo isolation hold. */
|
|
1696
|
+
const CI_SECURITY_OTHER_REPO = "other/repo";
|
|
1651
1697
|
async function seedCiSecurityFixturesDirect(databaseUrl, opts) {
|
|
1652
1698
|
const contextName = opts.contextName ?? "ci-security-env";
|
|
1653
1699
|
const sourceName = opts.sourceName ?? "ci-security-dashboard-resolver";
|
|
@@ -1665,33 +1711,85 @@ async function seedCiSecurityFixturesDirect(databaseUrl, opts) {
|
|
|
1665
1711
|
VALUES ($1, $2, \'fixed\', true)
|
|
1666
1712
|
ON CONFLICT (org_id, name) DO UPDATE SET enabled = true
|
|
1667
1713
|
RETURNING id`, [opts.orgId, contextName])).rows[0].id;
|
|
1714
|
+
async function seedPrHold(args) {
|
|
1715
|
+
await pool.query(`INSERT INTO execution_runs (
|
|
1716
|
+
run_id, workflow_name, provider, repo_identifier,
|
|
1717
|
+
ref, sha, delivery_id, status, trust_tier, lock_file_source,
|
|
1718
|
+
contributor_username, routing_key, pr_number
|
|
1719
|
+
) VALUES ($1, \'e2e-security-wf\', \'internal\', $4, \'refs/heads/feature\',
|
|
1720
|
+
$5, $2, \'pending\', \'unknown\', \'base\', \'unknown-dev\', $3, $6)`, [
|
|
1721
|
+
args.runId,
|
|
1722
|
+
args.deliveryId,
|
|
1723
|
+
opts.runsRoutingKey,
|
|
1724
|
+
args.repoIdentifier,
|
|
1725
|
+
args.sha,
|
|
1726
|
+
args.prNumber
|
|
1727
|
+
]);
|
|
1728
|
+
await pool.query(`INSERT INTO execution_jobs (job_id, run_id, job_name, status)
|
|
1729
|
+
VALUES ($1, $2, \'security-test-job\', \'pending\')`, [args.jobId, args.runId]);
|
|
1730
|
+
return (await pool.query(`INSERT INTO held_runs (org_id, run_id, job_id, context_id, hold_type, queue_type, reason, expires_at)
|
|
1731
|
+
VALUES ($1, $2, $3, $4, $5, \'security\', $6, NOW() + INTERVAL \'72 hours\')
|
|
1732
|
+
RETURNING id`, [
|
|
1733
|
+
opts.orgId,
|
|
1734
|
+
args.runId,
|
|
1735
|
+
args.jobId,
|
|
1736
|
+
args.contextId === void 0 ? envId : args.contextId,
|
|
1737
|
+
args.holdType ?? HoldType.enum.security,
|
|
1738
|
+
args.reason ?? unknownContributorHoldReason(contextName)
|
|
1739
|
+
])).rows[0].id;
|
|
1740
|
+
}
|
|
1741
|
+
const heldRunId = await seedPrHold({
|
|
1742
|
+
runId: opts.unknownRunId,
|
|
1743
|
+
deliveryId: opts.unknownDeliveryId,
|
|
1744
|
+
jobId: opts.unknownJobId,
|
|
1745
|
+
repoIdentifier: ".",
|
|
1746
|
+
prNumber: 1,
|
|
1747
|
+
sha: "abc123"
|
|
1748
|
+
});
|
|
1749
|
+
const secondHeldRunId = await seedPrHold({
|
|
1750
|
+
runId: opts.secondPrRunId,
|
|
1751
|
+
deliveryId: opts.secondPrDeliveryId,
|
|
1752
|
+
jobId: opts.secondPrJobId,
|
|
1753
|
+
repoIdentifier: ".",
|
|
1754
|
+
prNumber: 2,
|
|
1755
|
+
sha: "bbb222"
|
|
1756
|
+
});
|
|
1757
|
+
const otherRepoHeldRunId = await seedPrHold({
|
|
1758
|
+
runId: opts.otherRepoRunId,
|
|
1759
|
+
deliveryId: opts.otherRepoDeliveryId,
|
|
1760
|
+
jobId: opts.otherRepoJobId,
|
|
1761
|
+
repoIdentifier: CI_SECURITY_OTHER_REPO,
|
|
1762
|
+
prNumber: 1,
|
|
1763
|
+
sha: "ccc333"
|
|
1764
|
+
});
|
|
1765
|
+
const wfModHeldRunId = await seedPrHold({
|
|
1766
|
+
runId: opts.wfModRunId,
|
|
1767
|
+
deliveryId: opts.wfModDeliveryId,
|
|
1768
|
+
jobId: opts.wfModJobId,
|
|
1769
|
+
repoIdentifier: ".",
|
|
1770
|
+
prNumber: 3,
|
|
1771
|
+
sha: "ddd444",
|
|
1772
|
+
holdType: HoldType.enum.security,
|
|
1773
|
+
reason: "workflow_modification",
|
|
1774
|
+
contextId: null
|
|
1775
|
+
});
|
|
1776
|
+
const forkPrHeldRunId = await seedPrHold({
|
|
1777
|
+
runId: opts.forkPrRunId,
|
|
1778
|
+
deliveryId: opts.forkPrDeliveryId,
|
|
1779
|
+
jobId: opts.forkPrJobId,
|
|
1780
|
+
repoIdentifier: ".",
|
|
1781
|
+
prNumber: 4,
|
|
1782
|
+
sha: "eee555",
|
|
1783
|
+
holdType: HoldType.enum.security,
|
|
1784
|
+
reason: "fork_pr",
|
|
1785
|
+
contextId: null
|
|
1786
|
+
});
|
|
1668
1787
|
await pool.query(`INSERT INTO execution_runs (
|
|
1669
1788
|
run_id, workflow_name, provider, repo_identifier,
|
|
1670
1789
|
ref, sha, delivery_id, status, trust_tier, lock_file_source,
|
|
1671
|
-
contributor_username, routing_key
|
|
1672
|
-
) VALUES ($1, \'e2e-security-wf\', \'internal\', \'.\', \'refs/heads/feature\',
|
|
1673
|
-
\'abc123\', $2, \'pending\', \'unknown\', \'base\', \'unknown-dev\', $3)`, [
|
|
1674
|
-
opts.unknownRunId,
|
|
1675
|
-
opts.unknownDeliveryId,
|
|
1676
|
-
opts.runsRoutingKey
|
|
1677
|
-
]);
|
|
1678
|
-
await pool.query(`INSERT INTO execution_jobs (job_id, run_id, job_name, status)
|
|
1679
|
-
VALUES ($1, $2, \'security-test-job\', \'pending\')`, [opts.unknownJobId, opts.unknownRunId]);
|
|
1680
|
-
const heldRunId = (await pool.query(`INSERT INTO held_runs (org_id, run_id, job_id, context_id, hold_type, queue_type, reason, expires_at)
|
|
1681
|
-
VALUES ($1, $2, $3, $4, \'unknown_contributor\', \'security\',
|
|
1682
|
-
\'Unknown contributor requires approval\', NOW() + INTERVAL \'72 hours\')
|
|
1683
|
-
RETURNING id`, [
|
|
1684
|
-
opts.orgId,
|
|
1685
|
-
opts.unknownRunId,
|
|
1686
|
-
opts.unknownJobId,
|
|
1687
|
-
envId
|
|
1688
|
-
])).rows[0].id;
|
|
1689
|
-
await pool.query(`INSERT INTO execution_runs (
|
|
1690
|
-
run_id, workflow_name, provider, repo_identifier,
|
|
1691
|
-
ref, sha, delivery_id, status, trust_tier, lock_file_source,
|
|
1692
|
-
contributor_username, routing_key
|
|
1790
|
+
contributor_username, routing_key, pr_number
|
|
1693
1791
|
) VALUES ($1, \'e2e-security-wf\', \'internal\', \'.\', \'refs/heads/feature\',
|
|
1694
|
-
\'def456\', $2, \'running\', \'trusted\', \'head\', \'trusted-dev\', $3)`, [
|
|
1792
|
+
\'def456\', $2, \'running\', \'trusted\', \'head\', \'trusted-dev\', $3, 1)`, [
|
|
1695
1793
|
opts.trustedRunId,
|
|
1696
1794
|
opts.trustedDeliveryId,
|
|
1697
1795
|
opts.runsRoutingKey
|
|
@@ -1699,31 +1797,43 @@ async function seedCiSecurityFixturesDirect(databaseUrl, opts) {
|
|
|
1699
1797
|
await pool.query(`INSERT INTO execution_jobs (job_id, run_id, job_name, status)
|
|
1700
1798
|
VALUES ($1, $2, \'security-test-job\', \'running\')`, [opts.trustedJobId, opts.trustedRunId]);
|
|
1701
1799
|
return {
|
|
1800
|
+
contextName,
|
|
1702
1801
|
envId,
|
|
1703
|
-
heldRunId
|
|
1802
|
+
heldRunId,
|
|
1803
|
+
secondHeldRunId,
|
|
1804
|
+
otherRepoHeldRunId,
|
|
1805
|
+
wfModHeldRunId,
|
|
1806
|
+
forkPrHeldRunId
|
|
1704
1807
|
};
|
|
1705
1808
|
} finally {
|
|
1706
1809
|
await pool.end();
|
|
1707
1810
|
}
|
|
1708
1811
|
}
|
|
1709
1812
|
/**
|
|
1710
|
-
* Poll `execution_runs` for
|
|
1711
|
-
* `
|
|
1712
|
-
*
|
|
1813
|
+
* Poll `execution_runs` for the newest run started since `since` whose status
|
|
1814
|
+
* is in `statuses`, returning that status. Resolves `{ status: null }` if the
|
|
1815
|
+
* deadline passes before any run reaches a target status.
|
|
1816
|
+
*
|
|
1817
|
+
* Callers wanting "did the run finish?" pass the terminal status set and read
|
|
1818
|
+
* the landed status — a terminal failure is reported immediately rather than
|
|
1819
|
+
* indistinguishable from a timeout. Used by the cluster reroute tests to gate
|
|
1820
|
+
* on a workflow reaching a terminal state after a webhook trigger.
|
|
1713
1821
|
*/
|
|
1714
|
-
async function
|
|
1715
|
-
const timeoutMs = opts.timeoutMs ??
|
|
1822
|
+
async function waitForExecutionRunReachesStatusSinceDirect(databaseUrl, opts) {
|
|
1823
|
+
const timeoutMs = opts.timeoutMs ?? 24e4;
|
|
1716
1824
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
1717
1825
|
const deadline = Date.now() + timeoutMs;
|
|
1718
1826
|
const pool = createPool(databaseUrl);
|
|
1719
1827
|
try {
|
|
1720
1828
|
while (Date.now() < deadline) {
|
|
1721
|
-
|
|
1722
|
-
WHERE started_at > $1 AND status = $2
|
|
1723
|
-
|
|
1829
|
+
const result = await pool.query(`SELECT status FROM execution_runs
|
|
1830
|
+
WHERE started_at > $1 AND status = ANY($2)
|
|
1831
|
+
ORDER BY started_at DESC
|
|
1832
|
+
LIMIT 1`, [opts.since, [...opts.statuses]]);
|
|
1833
|
+
if (result.rows.length > 0) return { status: result.rows[0].status };
|
|
1724
1834
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
1725
1835
|
}
|
|
1726
|
-
return {
|
|
1836
|
+
return { status: null };
|
|
1727
1837
|
} finally {
|
|
1728
1838
|
await pool.end();
|
|
1729
1839
|
}
|
|
@@ -2269,11 +2379,15 @@ async function bumpRegistryVersionSimpleDirect(databaseUrl, opts = {}) {
|
|
|
2269
2379
|
async function upsertCronLastFiredDirect(databaseUrl, opts) {
|
|
2270
2380
|
const pool = createPool(databaseUrl);
|
|
2271
2381
|
try {
|
|
2272
|
-
await pool.query(`INSERT INTO cron_last_fired (registration_id, last_fired_at)
|
|
2273
|
-
VALUES ($1, NOW() - ($2)::interval)
|
|
2274
|
-
ON CONFLICT (registration_id) DO UPDATE SET
|
|
2382
|
+
await pool.query(`INSERT INTO cron_last_fired (registration_id, schedule_key, last_fired_at)
|
|
2383
|
+
VALUES ($1, $3, NOW() - ($2)::interval)
|
|
2384
|
+
ON CONFLICT (registration_id, schedule_key) DO UPDATE SET
|
|
2275
2385
|
last_fired_at = NOW() - ($2)::interval,
|
|
2276
|
-
updated_at = NOW()`, [
|
|
2386
|
+
updated_at = NOW()`, [
|
|
2387
|
+
opts.registrationId,
|
|
2388
|
+
opts.agoInterval,
|
|
2389
|
+
opts.scheduleKey
|
|
2390
|
+
]);
|
|
2277
2391
|
} finally {
|
|
2278
2392
|
await pool.end();
|
|
2279
2393
|
}
|
|
@@ -2297,7 +2411,7 @@ async function countCronLastFiredDirect(databaseUrl, opts) {
|
|
|
2297
2411
|
async function insertCronLastFiredNowDirect(databaseUrl, opts) {
|
|
2298
2412
|
const pool = createPool(databaseUrl);
|
|
2299
2413
|
try {
|
|
2300
|
-
await pool.query(`INSERT INTO cron_last_fired (registration_id, last_fired_at) VALUES ($1, NOW())`, [opts.registrationId]);
|
|
2414
|
+
await pool.query(`INSERT INTO cron_last_fired (registration_id, schedule_key, last_fired_at) VALUES ($1, $2, NOW())`, [opts.registrationId, opts.scheduleKey]);
|
|
2301
2415
|
} finally {
|
|
2302
2416
|
await pool.end();
|
|
2303
2417
|
}
|
|
@@ -2754,6 +2868,6 @@ async function terminateIdleDbBackendsDirect(databaseUrl) {
|
|
|
2754
2868
|
}
|
|
2755
2869
|
}
|
|
2756
2870
|
//#endregion
|
|
2757
|
-
export { MIGRATION_HASH_TABLE, PROVIDER_HASH_KEY, REGISTERABLE_TRIGGER_TYPES, apiKeyExistsDirect, bumpRegistryVersionDirect, bumpRegistryVersionSimpleDirect, cleanupExecutionRowsDirect, clearDispatchQueueDirect, clearPeerCredentialsRevokedAtByIdsDirect, computeMigrationsHash, countActivePeerCredentialsByInstanceDirect, countCronLastFiredDirect, countHeldRunsByRunIdDirect, countWebhookSourcesByConnectionIdDirect, createContextTemplateDirect, createDbRole, createJoinTokenDirect, createReadOnlyDbUser, deleteContextDirect, deleteCronLastFiredDirect, deleteCrossRepoTrustDirect, deleteExecutionRunsByWorkflowNameDirect, deleteGenericWebhookSourcesByNameDirect, deleteJoinTokensByCreatedByDirect, deleteKiciEventsDirect, deleteOrgSettingsByCustomerIdDirect, deletePeerCredentialsByInstanceIdLikeDirect, deleteWorkflowRegistrationsDirect, describeTableColumnsDirect, dropAndCreateDatabase, dropDatabaseDirect, emitKiciEventDirect, ensureDatabase, ensureOrgOwnerMemberDirect, findAnyUserApiKeyIdDirect, getExecutionRunSecurityDirect, getGenericWebhookSourceByRoutingKeyDirect, getHeldRunByIdDirect, getPeerCredentialRevokedAtDirect, getRegistryVersionDirect, getWebhookSourceByRoutingKeyDirect, getWorkflowRegistrationByIdDirect, insertCronLastFiredNowDirect, insertCrossRepoTrustStrictDirect, insertKiciEventAtDirect, insertKiciEventRawDirect, insertPeerCredentialExpiredDirect, insertWorkflowRegistrationRawDirect, insertWorkflowRegistrationStrictDirect, isSchemaCurrent, isSchemaCurrentFromFilesDirect, latestExecutionRunByStatusDirect, listActiveGenericWebhookSourcesDirect, listActivePeerCredentialsExcludingDirect, listContextsDirect, listCrossRepoTrustBySourceRoutingKeyDirect, listExecutionJobsDirect, listExecutionRunsDirect, listHeldRunApprovalsDirect, listKiciEventsDirect, listQueueDirect, listRegistrationsByRoutingKeyDirect, listRegistrationsDirect, maskDatabaseUrl, paginateUnprocessedEventsKeysetDirect, parseDatabaseUrl, platformConnectionExistsDirect, pollKiciEventsDirect, prunePeerCredentialsDirect, purgeContextsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, readStoredMigrationContentHash, registerWorkflowManualDirect, resetRaftStateDirect, resolvePlatformWebhookSourceRoutingKeyDirect, restoreSoftDeletedGenericWebhookSourceDirect, seedApiKeyInlineDirect, seedCiSecurityFixturesDirect, seedContextBindingDirect, seedContextDirect, seedCrossRepoTrustDirect, seedGenericWebhookSourceDirect, seedSourcePrivateKeyDirect, seedSyntheticGithubSourceDirect, seedUniversalGitSourceDirect, seedWebhookSecretDirect, setContextPolicyDirect, setContextSecretDirect, showContextDirect, showExecutionRunDirect, showKiciEventDirect, showQueueEntryDirect, showRegistrationDirect, storeMigrationContentHash, storeMigrationContentHashInTableDirect, tableExistsDirect, terminateIdleDbBackendsDirect, updateGenericWebhookVerificationConfigDirect, updateOrgSettingsDeniedReposDirect, updateSourceRoutingKeyDirect, updateWorkflowRegistrationCommitShaDirect, upsertCronLastFiredDirect, upsertOrgSettingsGlobalWorkflowsDirect, verifyKiciEventNotifyDirect, waitForEventLogRowByDeliveryIdDirect,
|
|
2871
|
+
export { CI_SECURITY_OTHER_REPO, MIGRATION_HASH_TABLE, PROVIDER_HASH_KEY, REGISTERABLE_TRIGGER_TYPES, apiKeyExistsDirect, bumpRegistryVersionDirect, bumpRegistryVersionSimpleDirect, cleanupExecutionRowsDirect, clearDispatchQueueDirect, clearPeerCredentialsRevokedAtByIdsDirect, computeMigrationsHash, countActivePeerCredentialsByInstanceDirect, countCronLastFiredDirect, countHeldRunsByRunIdDirect, countWebhookSourcesByConnectionIdDirect, createContextTemplateDirect, createDbRole, createJoinTokenDirect, createReadOnlyDbUser, deleteContextDirect, deleteCronLastFiredDirect, deleteCrossRepoTrustDirect, deleteExecutionRunsByWorkflowNameDirect, deleteGenericWebhookSourcesByNameDirect, deleteJoinTokensByCreatedByDirect, deleteKiciEventsDirect, deleteOrgSettingsByCustomerIdDirect, deletePeerCredentialsByInstanceIdLikeDirect, deleteWorkflowRegistrationsDirect, describeTableColumnsDirect, dropAndCreateDatabase, dropDatabaseDirect, emitKiciEventDirect, ensureDatabase, ensureOrgOwnerMemberDirect, findAnyUserApiKeyIdDirect, getExecutionRunSecurityDirect, getGenericWebhookSourceByRoutingKeyDirect, getHeldRunByIdDirect, getPeerCredentialRevokedAtDirect, getRegistryVersionDirect, getWebhookSourceByRoutingKeyDirect, getWorkflowRegistrationByIdDirect, insertCronLastFiredNowDirect, insertCrossRepoTrustStrictDirect, insertKiciEventAtDirect, insertKiciEventRawDirect, insertPeerCredentialExpiredDirect, insertWorkflowRegistrationRawDirect, insertWorkflowRegistrationStrictDirect, isSchemaCurrent, isSchemaCurrentFromFilesDirect, latestExecutionRunByStatusDirect, listActiveGenericWebhookSourcesDirect, listActivePeerCredentialsExcludingDirect, listCheckRunTrackingDirect, listContextsDirect, listCrossRepoTrustBySourceRoutingKeyDirect, listExecutionJobsDirect, listExecutionRunsDirect, listHeldRunApprovalsDirect, listKiciEventsDirect, listQueueDirect, listRegistrationsByRoutingKeyDirect, listRegistrationsDirect, maskDatabaseUrl, paginateUnprocessedEventsKeysetDirect, parseDatabaseUrl, platformConnectionExistsDirect, pollKiciEventsDirect, prunePeerCredentialsDirect, purgeContextsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, readStoredMigrationContentHash, registerWorkflowManualDirect, resetRaftStateDirect, resolvePlatformWebhookSourceRoutingKeyDirect, restoreSoftDeletedGenericWebhookSourceDirect, seedApiKeyInlineDirect, seedCiSecurityFixturesDirect, seedContextBindingDirect, seedContextDirect, seedCrossRepoTrustDirect, seedGenericWebhookSourceDirect, seedSourcePrivateKeyDirect, seedSyntheticGithubSourceDirect, seedUniversalGitSourceDirect, seedWebhookSecretDirect, setContextPolicyDirect, setContextSecretDirect, showContextDirect, showExecutionRunDirect, showKiciEventDirect, showQueueEntryDirect, showRegistrationDirect, storeMigrationContentHash, storeMigrationContentHashInTableDirect, tableExistsDirect, terminateIdleDbBackendsDirect, updateGenericWebhookVerificationConfigDirect, updateOrgSettingsDeniedReposDirect, updateSourceRoutingKeyDirect, updateWorkflowRegistrationCommitShaDirect, upsertCronLastFiredDirect, upsertOrgSettingsGlobalWorkflowsDirect, verifyKiciEventNotifyDirect, waitForEventLogRowByDeliveryIdDirect, waitForExecutionRunReachesStatusSinceDirect, waitForLatestExecutionJobStatusDirect, waitForPlatformEventLogDistinctRoutedDirect, waitForPlatformExecutionRunStatusDirect, waitForPlatformRegistrationsDirect, waitForPostgresDirect, waitForRegistrationsByRoutingKeyDirect, waitForRegistrationsUpdatedAtAdvanceDirect, waitForRunCompletionDirect };
|
|
2758
2872
|
|
|
2759
2873
|
//# sourceMappingURL=db-admin.js.map
|
package/dist/db.d.ts
CHANGED
|
@@ -2,6 +2,15 @@ import pg from 'pg';
|
|
|
2
2
|
import { Kysely } from 'kysely';
|
|
3
3
|
/** Where a pg connection error surfaced. */
|
|
4
4
|
export type PgPoolErrorSource = 'idle-pool' | 'client';
|
|
5
|
+
/**
|
|
6
|
+
* Outcome of a single pool acquire.
|
|
7
|
+
*
|
|
8
|
+
* `'timeout'` means the caller waited the pool's full `connectionTimeoutMillis`
|
|
9
|
+
* and was refused a connection — a load condition. A backend that cannot be
|
|
10
|
+
* reached rejects with a connection error instead and is deliberately NOT
|
|
11
|
+
* reported here; that is a different condition with a different owner.
|
|
12
|
+
*/
|
|
13
|
+
export type PoolAcquireOutcome = 'ok' | 'timeout';
|
|
5
14
|
export interface CreatePoolOptions {
|
|
6
15
|
/** Extra pg.Pool config merged over the connection string (e.g. max, connectionTimeoutMillis). */
|
|
7
16
|
config?: Omit<pg.PoolConfig, 'connectionString'>;
|
|
@@ -11,6 +20,23 @@ export interface CreatePoolOptions {
|
|
|
11
20
|
* never replaces the log.
|
|
12
21
|
*/
|
|
13
22
|
onError?: (err: Error, source: PgPoolErrorSource) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Optional hook invoked once per `pool.connect()` acquire on this pool, with
|
|
25
|
+
* the outcome and how long the caller waited.
|
|
26
|
+
*
|
|
27
|
+
* Only the promise form of `connect` is instrumented, so an acquire made via
|
|
28
|
+
* `pool.query(...)` is NOT reported — pg implements `query` on top of the
|
|
29
|
+
* callback form of `connect`. The exclusion is symmetric (neither outcome is
|
|
30
|
+
* reported), so a ratio derived from this hook stays well-formed; but work
|
|
31
|
+
* whose acquires must be counted has to go through `pool.connect()`, as
|
|
32
|
+
* Kysely's PostgresDialect does.
|
|
33
|
+
*
|
|
34
|
+
* Opt-in: a pool created without it behaves exactly as before. A consumer
|
|
35
|
+
* that derives a load signal from acquire outcomes wires it on the pool whose
|
|
36
|
+
* saturation actually matters to it — one hook shared across unrelated pools
|
|
37
|
+
* would attribute one pool's exhaustion to another pool's traffic.
|
|
38
|
+
*/
|
|
39
|
+
onAcquire?: (outcome: PoolAcquireOutcome, waitedMs: number) => void;
|
|
14
40
|
}
|
|
15
41
|
/**
|
|
16
42
|
* Create PostgreSQL connection pool.
|
|
@@ -23,6 +49,14 @@ export interface CreatePoolOptions {
|
|
|
23
49
|
* the next acquire. In-flight query failures still reject to their callers.
|
|
24
50
|
*/
|
|
25
51
|
export declare function createPool(databaseUrl: string, options?: CreatePoolOptions): pg.Pool;
|
|
52
|
+
/**
|
|
53
|
+
* node-postgres rejects a pool-acquire timeout (the `connectionTimeoutMillis`
|
|
54
|
+
* window elapsed with no free connection) with this exact message. It is the
|
|
55
|
+
* only signal pg exposes to tell "pool busy (load)" apart from "backend
|
|
56
|
+
* unreachable". Centralized here + covered by one unit test so a pg-version
|
|
57
|
+
* bump that changes the string fails loudly in one place.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isPoolAcquireTimeout(err: unknown): boolean;
|
|
26
60
|
/**
|
|
27
61
|
* Create Kysely database instance (PostgreSQL only).
|
|
28
62
|
*
|
package/dist/db.js
CHANGED
|
@@ -38,9 +38,38 @@ function createPool(databaseUrl, options) {
|
|
|
38
38
|
pool.on("connect", (client) => {
|
|
39
39
|
client.on("error", (err) => handle(err, "client"));
|
|
40
40
|
});
|
|
41
|
+
if (options?.onAcquire) {
|
|
42
|
+
const report = options.onAcquire;
|
|
43
|
+
const original = pool.connect.bind(pool);
|
|
44
|
+
pool.connect = function connect(cb) {
|
|
45
|
+
if (typeof cb === "function") return original(cb);
|
|
46
|
+
const startedAt = Date.now();
|
|
47
|
+
return original().then((client) => {
|
|
48
|
+
try {
|
|
49
|
+
report("ok", Date.now() - startedAt);
|
|
50
|
+
} catch {}
|
|
51
|
+
return client;
|
|
52
|
+
}, (err) => {
|
|
53
|
+
try {
|
|
54
|
+
if (isPoolAcquireTimeout(err)) report("timeout", Date.now() - startedAt);
|
|
55
|
+
} catch {}
|
|
56
|
+
throw err;
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
}
|
|
41
60
|
return pool;
|
|
42
61
|
}
|
|
43
62
|
/**
|
|
63
|
+
* node-postgres rejects a pool-acquire timeout (the `connectionTimeoutMillis`
|
|
64
|
+
* window elapsed with no free connection) with this exact message. It is the
|
|
65
|
+
* only signal pg exposes to tell "pool busy (load)" apart from "backend
|
|
66
|
+
* unreachable". Centralized here + covered by one unit test so a pg-version
|
|
67
|
+
* bump that changes the string fails loudly in one place.
|
|
68
|
+
*/
|
|
69
|
+
function isPoolAcquireTimeout(err) {
|
|
70
|
+
return err instanceof Error && err.message === "timeout exceeded when trying to connect";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
44
73
|
* Create Kysely database instance (PostgreSQL only).
|
|
45
74
|
*
|
|
46
75
|
* Generic over the database type so each consumer can provide
|
|
@@ -50,6 +79,6 @@ function createDb(pool) {
|
|
|
50
79
|
return new Kysely({ dialect: new PostgresDialect({ pool }) });
|
|
51
80
|
}
|
|
52
81
|
//#endregion
|
|
53
|
-
export { createDb, createPool };
|
|
82
|
+
export { createDb, createPool, isPoolAcquireTimeout };
|
|
54
83
|
|
|
55
84
|
//# sourceMappingURL=db.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
3
|
+
import * as path$1 from "node:path";
|
|
4
4
|
//#region src/diagnostics/bundle-archive.ts
|
|
5
5
|
/**
|
|
6
6
|
* Shared debug-bundle archive primitives.
|
|
@@ -103,18 +103,18 @@ async function addLogsToArchive(archive, logDir, logWindowHours) {
|
|
|
103
103
|
const cutoff = Date.now() - logWindowHours * 60 * 60 * 1e3;
|
|
104
104
|
const entries = fs.readdirSync(logDir).filter((f) => {
|
|
105
105
|
if (!f.endsWith(".log")) return false;
|
|
106
|
-
return fs.statSync(path.join(logDir, f)).mtimeMs >= cutoff;
|
|
106
|
+
return fs.statSync(path$1.join(logDir, f)).mtimeMs >= cutoff;
|
|
107
107
|
});
|
|
108
108
|
entries.sort((a, b) => {
|
|
109
|
-
const aStat = fs.statSync(path.join(logDir, a));
|
|
110
|
-
return fs.statSync(path.join(logDir, b)).mtimeMs - aStat.mtimeMs;
|
|
109
|
+
const aStat = fs.statSync(path$1.join(logDir, a));
|
|
110
|
+
return fs.statSync(path$1.join(logDir, b)).mtimeMs - aStat.mtimeMs;
|
|
111
111
|
});
|
|
112
112
|
let totalBytes = 0;
|
|
113
113
|
let totalLines = 0;
|
|
114
114
|
let errors = 0;
|
|
115
115
|
let warnings = 0;
|
|
116
116
|
for (const entry of entries) {
|
|
117
|
-
const filePath = path.join(logDir, entry);
|
|
117
|
+
const filePath = path$1.join(logDir, entry);
|
|
118
118
|
const stat = fs.statSync(filePath);
|
|
119
119
|
if (totalBytes + stat.size > 52428800) break;
|
|
120
120
|
const content = fs.readFileSync(filePath, "utf-8");
|
package/dist/env/allowlist.d.ts
CHANGED
|
@@ -71,6 +71,11 @@
|
|
|
71
71
|
* dashboard's vite.config consults to disable
|
|
72
72
|
* the dev proxy), HEADED (Playwright convention
|
|
73
73
|
* for `--headed` runs).
|
|
74
|
+
* - Vitest runtime — VITEST_* (vitest's own names, which it reads
|
|
75
|
+
* after config resolution: VITEST_MAX_WORKERS
|
|
76
|
+
* carries the per-process worker ceiling
|
|
77
|
+
* `hack/lib/vitest-workers.ts` publishes, and
|
|
78
|
+
* VITEST_POOL_ID marks a pool worker).
|
|
74
79
|
*/
|
|
75
80
|
export declare const OS_SDK_ALLOWLIST_REGEX: RegExp;
|
|
76
81
|
/**
|
package/dist/env/allowlist.js
CHANGED
|
@@ -73,8 +73,13 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
73
73
|
* dashboard's vite.config consults to disable
|
|
74
74
|
* the dev proxy), HEADED (Playwright convention
|
|
75
75
|
* for `--headed` runs).
|
|
76
|
+
* - Vitest runtime — VITEST_* (vitest's own names, which it reads
|
|
77
|
+
* after config resolution: VITEST_MAX_WORKERS
|
|
78
|
+
* carries the per-process worker ceiling
|
|
79
|
+
* `hack/lib/vitest-workers.ts` publishes, and
|
|
80
|
+
* VITEST_POOL_ID marks a pool worker).
|
|
76
81
|
*/
|
|
77
|
-
const OS_SDK_ALLOWLIST_REGEX = /^(KICI_.*|NODE_ENV|HOME|PATH|TZ|LANG|TMPDIR|USER|USERNAME|SHELL|COMSPEC|PWD|OLDPWD|HOSTNAME|PROCESSOR_ARCHITECTURE|COLUMNS|LINES|TERM|COLORTERM|DISPLAY|WAYLAND_DISPLAY|LOCALAPPDATA|XDG_CACHE_HOME|XDG_CONFIG_HOME|XDG_DATA_HOME|XDG_RUNTIME_DIR|XDG_STATE_HOME|INIT_CWD|npm_.*|SSH_.*|CI|GITHUB_ACTIONS|GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH|GITHUB_STEP_SUMMARY|GITLAB_CI|AWS_.*|REDIS_.*|OTEL_.*|STRIPE_.*|DOCKER_.*|GIT_.*|CONTAINER_HOST|container|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PGSERVICEFILE|PGSSLMODE|FORGEJO_URL|FORGEJO_CONTAINER|KEYCLOAK_.*|VITE_.*|PLAYWRIGHT|HEADED)$/;
|
|
82
|
+
const OS_SDK_ALLOWLIST_REGEX = /^(KICI_.*|NODE_ENV|HOME|PATH|TZ|LANG|TMPDIR|USER|USERNAME|SHELL|COMSPEC|PWD|OLDPWD|HOSTNAME|PROCESSOR_ARCHITECTURE|COLUMNS|LINES|TERM|COLORTERM|DISPLAY|WAYLAND_DISPLAY|LOCALAPPDATA|XDG_CACHE_HOME|XDG_CONFIG_HOME|XDG_DATA_HOME|XDG_RUNTIME_DIR|XDG_STATE_HOME|INIT_CWD|npm_.*|SSH_.*|CI|GITHUB_ACTIONS|GITHUB_ENV|GITHUB_OUTPUT|GITHUB_PATH|GITHUB_STEP_SUMMARY|GITLAB_CI|AWS_.*|REDIS_.*|OTEL_.*|STRIPE_.*|DOCKER_.*|GIT_.*|CONTAINER_HOST|container|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PGSERVICEFILE|PGSSLMODE|FORGEJO_URL|FORGEJO_CONTAINER|KEYCLOAK_.*|VITE_.*|VITEST_.*|PLAYWRIGHT|HEADED)$/;
|
|
78
83
|
/**
|
|
79
84
|
* Returns true when `name` is allowed under the KiCI env-var convention:
|
|
80
85
|
* it matches the OS/SDK allowlist regex (which includes the `KICI_*`
|
package/dist/env/define-env.d.ts
CHANGED
|
@@ -66,8 +66,15 @@ export interface DefineEnvOptions<TShape extends z.ZodRawShape> {
|
|
|
66
66
|
descriptions?: Record<string, string>;
|
|
67
67
|
}
|
|
68
68
|
export interface DefineEnvResult<T> {
|
|
69
|
-
/**
|
|
70
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Parse `env` (defaults to `process.env`) into a typed config.
|
|
71
|
+
*
|
|
72
|
+
* `parserOverride` is an optional per-call parser (e.g. a scope-narrowed
|
|
73
|
+
* `.superRefine`) that replaces the default parser for this parse only —
|
|
74
|
+
* used to validate the same env map under a looser/stricter cross-field
|
|
75
|
+
* rule set without duplicating the envMap.
|
|
76
|
+
*/
|
|
77
|
+
parse(env?: NodeJS.ProcessEnv, parserOverride?: z.ZodType): T;
|
|
71
78
|
/** Machine-readable field specs for docs generation. */
|
|
72
79
|
describe(): EnvFieldSpec[];
|
|
73
80
|
/** Flat list of every env var the schema reads (for the unknown-var scanner). */
|
|
@@ -101,6 +108,16 @@ export declare function defineEnv<TShape extends z.ZodRawShape>(opts: DefineEnvO
|
|
|
101
108
|
* (on POSIX it's a shell-local var that doesn't leak).
|
|
102
109
|
* - `KICI_DEV`: the dev-mode toggle itself — read by the scanner to flip
|
|
103
110
|
* to warn-only, so it must not trip the scanner.
|
|
111
|
+
* - `KICI_BUILD_COUNTER_NO_COMMIT`: a build-tooling flag read only by
|
|
112
|
+
* `hack/lib/commit-build-counter.mjs` (skip the per-build `.build-counter`
|
|
113
|
+
* commit on a force-synced checkout, e.g. a remote E2E executor). Set in the
|
|
114
|
+
* ambient shell for `pnpm build`; the native orchestrator spawn inherits it.
|
|
115
|
+
* - `KICI_TEST_ISOLATION`: the test-isolation marker set at config-eval time
|
|
116
|
+
* by every vitest config in this repository (`hack/lib/vitest-isolation.ts`,
|
|
117
|
+
* enforced by `hack/check-vitest-isolation.ts`). It makes the CLI's
|
|
118
|
+
* `getConfigDir` refuse the developer machine's ambient `~/.kici` config, and
|
|
119
|
+
* it is inherited by every service a test spawns — same leak-by-inheritance
|
|
120
|
+
* shape as the `KICI_E2E_` prefix below.
|
|
104
121
|
*
|
|
105
122
|
* Keep this list small and well-justified. Every addition is a typo we can
|
|
106
123
|
* no longer catch, so only list things that are (a) actually set in the
|
package/dist/env/define-env.js
CHANGED
|
@@ -155,8 +155,8 @@ function describeFieldRecursive(shape, envMap, fieldPath, descriptions, out) {
|
|
|
155
155
|
* const config = envDef.parse();
|
|
156
156
|
*/
|
|
157
157
|
function defineEnv(opts) {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
function parse(env = process.env, parserOverride) {
|
|
159
|
+
const parserSchema = parserOverride ?? opts.parser ?? opts.schema;
|
|
160
160
|
const raw = readEnv(opts.envMap, env);
|
|
161
161
|
const result = parserSchema.safeParse(raw);
|
|
162
162
|
if (!result.success) {
|
|
@@ -227,12 +227,27 @@ function suggestClosest(name, candidates) {
|
|
|
227
227
|
* (on POSIX it's a shell-local var that doesn't leak).
|
|
228
228
|
* - `KICI_DEV`: the dev-mode toggle itself — read by the scanner to flip
|
|
229
229
|
* to warn-only, so it must not trip the scanner.
|
|
230
|
+
* - `KICI_BUILD_COUNTER_NO_COMMIT`: a build-tooling flag read only by
|
|
231
|
+
* `hack/lib/commit-build-counter.mjs` (skip the per-build `.build-counter`
|
|
232
|
+
* commit on a force-synced checkout, e.g. a remote E2E executor). Set in the
|
|
233
|
+
* ambient shell for `pnpm build`; the native orchestrator spawn inherits it.
|
|
234
|
+
* - `KICI_TEST_ISOLATION`: the test-isolation marker set at config-eval time
|
|
235
|
+
* by every vitest config in this repository (`hack/lib/vitest-isolation.ts`,
|
|
236
|
+
* enforced by `hack/check-vitest-isolation.ts`). It makes the CLI's
|
|
237
|
+
* `getConfigDir` refuse the developer machine's ambient `~/.kici` config, and
|
|
238
|
+
* it is inherited by every service a test spawns — same leak-by-inheritance
|
|
239
|
+
* shape as the `KICI_E2E_` prefix below.
|
|
230
240
|
*
|
|
231
241
|
* Keep this list small and well-justified. Every addition is a typo we can
|
|
232
242
|
* no longer catch, so only list things that are (a) actually set in the
|
|
233
243
|
* wild by our own tooling and (b) could never be a config typo.
|
|
234
244
|
*/
|
|
235
|
-
const RESERVED_NON_SCHEMA_KICI_VARS = [
|
|
245
|
+
const RESERVED_NON_SCHEMA_KICI_VARS = [
|
|
246
|
+
"KICI_CACHE",
|
|
247
|
+
"KICI_DEV",
|
|
248
|
+
"KICI_BUILD_COUNTER_NO_COMMIT",
|
|
249
|
+
"KICI_TEST_ISOLATION"
|
|
250
|
+
];
|
|
236
251
|
/**
|
|
237
252
|
* `KICI_*` prefixes that are entirely outside the service-config namespace —
|
|
238
253
|
* usually set by our own test / dev tooling and inherited into a child
|
package/dist/env/logger-env.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared LoggerEnv schema.
|
|
3
3
|
*
|
|
4
|
-
* The logger (packages/
|
|
5
|
-
* the per-service config loads
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* The logger (packages/core/src/logger.ts) reads these env vars *before*
|
|
5
|
+
* the per-service config loads — as does `kiciTmpBase()`
|
|
6
|
+
* (packages/core/src/tmp.ts) for the `KICI_TMPDIR` entry below — so we can't
|
|
7
|
+
* include them in the service schemas the normal way. We still want them in
|
|
8
|
+
* `docs/operator/env-reference.md` and in `validateUnknownKiciVars()`'s
|
|
9
|
+
* known-var set, so this schema documents them in one place. Each service
|
|
10
|
+
* includes the keys here when computing its "known KICI_* vars" list, and the
|
|
11
|
+
* docs generator emits a "Logger / shared" section from this schema.
|
|
11
12
|
*
|
|
12
13
|
* IMPORTANT: do not change the runtime behaviour of `logger.ts` from this
|
|
13
14
|
* schema — the schema is documentation + the unknown-var allowlist, not the
|
|
@@ -20,16 +21,17 @@ export declare const LoggerEnvSchema: z.ZodObject<{
|
|
|
20
21
|
KICI_LOG_MAX_SIZE: z.ZodDefault<z.ZodString>;
|
|
21
22
|
KICI_LOG_RETENTION_DAYS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
22
23
|
KICI_LOG_FORMAT: z.ZodDefault<z.ZodEnum<{
|
|
23
|
-
json: "json";
|
|
24
24
|
auto: "auto";
|
|
25
|
+
json: "json";
|
|
25
26
|
plain: "plain";
|
|
26
27
|
}>>;
|
|
27
28
|
KICI_CLUSTER_INSTANCE_ID: z.ZodOptional<z.ZodString>;
|
|
28
29
|
KICI_AGENT_ID: z.ZodOptional<z.ZodString>;
|
|
29
30
|
KICI_PLATFORM_INSTANCE_ID: z.ZodOptional<z.ZodString>;
|
|
31
|
+
KICI_TMPDIR: z.ZodOptional<z.ZodString>;
|
|
30
32
|
}, z.core.$strip>;
|
|
31
33
|
/** All env vars the logger reads, for the unknown-KICI-var scanner. */
|
|
32
|
-
export declare const LOGGER_ENV_VARS: readonly [
|
|
34
|
+
export declare const LOGGER_ENV_VARS: readonly ['KICI_LOG_DIR', 'KICI_LOG_MAX_SIZE', 'KICI_LOG_RETENTION_DAYS', 'KICI_LOG_FORMAT', 'KICI_CLUSTER_INSTANCE_ID', 'KICI_AGENT_ID', 'KICI_PLATFORM_INSTANCE_ID', 'KICI_TMPDIR'];
|
|
33
35
|
/** Doc-friendly description map (consumed by the env-reference generator). */
|
|
34
36
|
export declare const LOGGER_ENV_FIELD_SPECS: EnvFieldSpec[];
|
|
35
37
|
//# sourceMappingURL=logger-env.d.ts.map
|