@treeseed/sdk 0.8.1 → 0.8.3
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/operations/services/config-runtime.d.ts +2 -1
- package/dist/operations/services/config-runtime.js +21 -1
- package/dist/operations/services/github-automation.d.ts +10 -3
- package/dist/operations/services/github-automation.js +20 -8
- package/dist/operations/services/hosting-audit.d.ts +67 -0
- package/dist/operations/services/hosting-audit.js +642 -0
- package/dist/operations/services/hub-launch.js +2 -2
- package/dist/operations/services/hub-provider-launch.js +4 -4
- package/dist/operations/services/managed-host-security.d.ts +13 -0
- package/dist/operations/services/managed-host-security.js +53 -0
- package/dist/platform/env.yaml +49 -0
- package/dist/reconcile/builtin-adapters.js +5 -4
- package/dist/workflow/operations.d.ts +6 -0
- package/dist/workflow/operations.js +67 -2
- package/dist/workflow-support.d.ts +1 -0
- package/dist/workflow-support.js +8 -0
- package/package.json +1 -1
- package/templates/github/deploy.managed.workflow.yml +208 -0
|
@@ -315,12 +315,13 @@ export declare function checkTreeseedProviderConnections({ tenantRoot, scope, en
|
|
|
315
315
|
issues: any[];
|
|
316
316
|
}>;
|
|
317
317
|
export declare function formatTreeseedProviderConnectionReport(report: any): string;
|
|
318
|
-
export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, valuesOverlay, execution, concurrency, onProgress, }: {
|
|
318
|
+
export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, valuesOverlay, managedHostMode, execution, concurrency, onProgress, }: {
|
|
319
319
|
tenantRoot: string;
|
|
320
320
|
scope?: TreeseedConfigScope;
|
|
321
321
|
dryRun?: boolean;
|
|
322
322
|
repository?: string | null;
|
|
323
323
|
valuesOverlay?: Record<string, string | undefined>;
|
|
324
|
+
managedHostMode?: 'auto' | 'direct' | 'managed';
|
|
324
325
|
execution?: 'parallel' | 'sequential';
|
|
325
326
|
concurrency?: number;
|
|
326
327
|
onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
|
|
@@ -52,6 +52,10 @@ import {
|
|
|
52
52
|
resolveTreeseedToolBinary,
|
|
53
53
|
resolveTreeseedToolCommand
|
|
54
54
|
} from "../../managed-dependencies.js";
|
|
55
|
+
import {
|
|
56
|
+
filterManagedHostGitHubEnvironment,
|
|
57
|
+
usesManagedHostOperationRequests
|
|
58
|
+
} from "./managed-host-security.js";
|
|
55
59
|
import {
|
|
56
60
|
assertTreeseedKeyAgentResponse,
|
|
57
61
|
getTreeseedKeyAgentPaths,
|
|
@@ -1979,6 +1983,7 @@ async function syncTreeseedGitHubEnvironment({
|
|
|
1979
1983
|
dryRun = false,
|
|
1980
1984
|
repository: repositoryInput,
|
|
1981
1985
|
valuesOverlay = {},
|
|
1986
|
+
managedHostMode = "auto",
|
|
1982
1987
|
execution = "parallel",
|
|
1983
1988
|
concurrency = 4,
|
|
1984
1989
|
onProgress
|
|
@@ -1992,7 +1997,22 @@ async function syncTreeseedGitHubEnvironment({
|
|
|
1992
1997
|
...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
|
|
1993
1998
|
...nonEmptyEnvironmentValues(valuesOverlay)
|
|
1994
1999
|
};
|
|
1995
|
-
const
|
|
2000
|
+
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
2001
|
+
const managedBoundary = managedHostMode === "managed" || managedHostMode === "auto" && usesManagedHostOperationRequests(deployConfig);
|
|
2002
|
+
const allowed = managedBoundary ? filterManagedHostGitHubEnvironment({
|
|
2003
|
+
secrets: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-secret")).map((entry) => entry.id),
|
|
2004
|
+
variables: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-variable")).map((entry) => entry.id)
|
|
2005
|
+
}) : null;
|
|
2006
|
+
const allowedSecrets = allowed ? new Set(allowed.secrets) : null;
|
|
2007
|
+
const allowedVariables = allowed ? new Set(allowed.variables) : null;
|
|
2008
|
+
const relevant = registry.entries.filter((entry) => {
|
|
2009
|
+
if (!entry.scopes.includes(scope)) return false;
|
|
2010
|
+
if (!managedBoundary) return true;
|
|
2011
|
+
if (entry.sensitivity === "secret") {
|
|
2012
|
+
return Boolean(entry.targets.includes("github-secret") && allowedSecrets?.has(entry.id));
|
|
2013
|
+
}
|
|
2014
|
+
return Boolean(entry.targets.includes("github-variable") && allowedVariables?.has(entry.id));
|
|
2015
|
+
});
|
|
1996
2016
|
const ghToken = values.GH_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
|
|
1997
2017
|
const ghEnv = ghToken ? {
|
|
1998
2018
|
GH_TOKEN: ghToken,
|
|
@@ -68,22 +68,25 @@ export declare function initializeGitHubRepositoryWorkingTree(cwd: any, reposito
|
|
|
68
68
|
pushed: boolean;
|
|
69
69
|
};
|
|
70
70
|
export declare function resolveGitRepositoryRoot(tenantRoot: any): any;
|
|
71
|
-
export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purpose }?: {
|
|
71
|
+
export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purpose, managedHostMode }?: {
|
|
72
72
|
scope?: string | undefined;
|
|
73
73
|
purpose?: string | undefined;
|
|
74
|
+
managedHostMode?: string | undefined;
|
|
74
75
|
}): {
|
|
75
76
|
secrets: string[];
|
|
76
77
|
variables: string[];
|
|
77
78
|
};
|
|
78
79
|
export declare function requiredGitHubSecrets(tenantRoot: any): string[];
|
|
79
|
-
export declare function renderDeployWorkflow({ workingDirectory }: {
|
|
80
|
+
export declare function renderDeployWorkflow({ workingDirectory, executionBoundary }: {
|
|
80
81
|
workingDirectory: any;
|
|
82
|
+
executionBoundary?: string | undefined;
|
|
81
83
|
}): string;
|
|
82
84
|
export declare function renderHostedProjectWorkflow({ workingDirectory }: {
|
|
83
85
|
workingDirectory: any;
|
|
84
86
|
}): string;
|
|
85
87
|
export declare function ensureDeployWorkflow(tenantRoot: any): {
|
|
86
88
|
workingDirectory: string;
|
|
89
|
+
executionBoundary: string;
|
|
87
90
|
workflowPath: string;
|
|
88
91
|
changed: boolean;
|
|
89
92
|
};
|
|
@@ -94,6 +97,7 @@ export declare function ensureHostedProjectWorkflow(tenantRoot: any): {
|
|
|
94
97
|
};
|
|
95
98
|
export declare function ensureStandardizedGitHubWorkflows(tenantRoot: any): {
|
|
96
99
|
workingDirectory: string;
|
|
100
|
+
executionBoundary: string;
|
|
97
101
|
workflowPath: string;
|
|
98
102
|
changed: boolean;
|
|
99
103
|
}[];
|
|
@@ -109,11 +113,12 @@ export declare function ensureGitHubSecrets(tenantRoot: any, { dryRun }?: {
|
|
|
109
113
|
existing: string[];
|
|
110
114
|
created: string[];
|
|
111
115
|
}>;
|
|
112
|
-
export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose, valuesOverlay }?: {
|
|
116
|
+
export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose, valuesOverlay, managedHostMode }?: {
|
|
113
117
|
dryRun?: boolean | undefined;
|
|
114
118
|
scope?: string | undefined;
|
|
115
119
|
purpose?: string | undefined;
|
|
116
120
|
valuesOverlay?: {} | undefined;
|
|
121
|
+
managedHostMode?: string | undefined;
|
|
117
122
|
}): Promise<{
|
|
118
123
|
repository: null;
|
|
119
124
|
secrets: {
|
|
@@ -144,11 +149,13 @@ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun,
|
|
|
144
149
|
mode: string;
|
|
145
150
|
workflow: {
|
|
146
151
|
workingDirectory: string;
|
|
152
|
+
executionBoundary: string;
|
|
147
153
|
workflowPath: string;
|
|
148
154
|
changed: boolean;
|
|
149
155
|
};
|
|
150
156
|
workflows: {
|
|
151
157
|
workingDirectory: string;
|
|
158
|
+
executionBoundary: string;
|
|
152
159
|
workflowPath: string;
|
|
153
160
|
changed: boolean;
|
|
154
161
|
}[];
|
|
@@ -3,6 +3,10 @@ import { dirname, relative, resolve } from "node:path";
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { resolveTreeseedEnvironmentRegistry } from "../../platform/environment.js";
|
|
5
5
|
import { packageRoot, loadCliDeployConfig } from "./runtime-tools.js";
|
|
6
|
+
import {
|
|
7
|
+
filterManagedHostGitHubEnvironment,
|
|
8
|
+
usesManagedHostOperationRequests
|
|
9
|
+
} from "./managed-host-security.js";
|
|
6
10
|
import {
|
|
7
11
|
createGitHubApiClient,
|
|
8
12
|
ensureGitHubRepository,
|
|
@@ -256,16 +260,18 @@ function resolveGitRepositoryRoot(tenantRoot) {
|
|
|
256
260
|
const result = runGit(["rev-parse", "--show-toplevel"], { cwd: tenantRoot, allowFailure: true });
|
|
257
261
|
return result.status === 0 ? result.stdout.trim() : tenantRoot;
|
|
258
262
|
}
|
|
259
|
-
function requiredGitHubEnvironment(tenantRoot, { scope = "prod", purpose = "save" } = {}) {
|
|
263
|
+
function requiredGitHubEnvironment(tenantRoot, { scope = "prod", purpose = "save", managedHostMode = "auto" } = {}) {
|
|
260
264
|
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
261
265
|
const registry = resolveTreeseedEnvironmentRegistry({ deployConfig });
|
|
262
266
|
const relevant = registry.entries.filter(
|
|
263
267
|
(entry) => entry.scopes.includes(scope) && entry.purposes.includes(purpose) && (!entry.isRelevant || entry.isRelevant(registry.context, scope, purpose))
|
|
264
268
|
);
|
|
265
|
-
|
|
269
|
+
const required = {
|
|
266
270
|
secrets: [...new Set(relevant.filter((entry) => entry.targets.includes("github-secret")).map((entry) => entry.id))],
|
|
267
271
|
variables: [...new Set(relevant.filter((entry) => entry.targets.includes("github-variable")).map((entry) => entry.id))]
|
|
268
272
|
};
|
|
273
|
+
const managedBoundary = managedHostMode === "managed" || managedHostMode === "auto" && usesManagedHostOperationRequests(deployConfig);
|
|
274
|
+
return managedBoundary ? filterManagedHostGitHubEnvironment(required) : required;
|
|
269
275
|
}
|
|
270
276
|
function requiredGitHubSecrets(tenantRoot) {
|
|
271
277
|
return requiredGitHubEnvironment(tenantRoot).secrets;
|
|
@@ -299,8 +305,11 @@ function renderWorkflowTemplate(templateName, { workingDirectory }) {
|
|
|
299
305
|
normalizedWorkingDirectory === "." ? "package-lock.json" : `${normalizedWorkingDirectory}/package-lock.json`
|
|
300
306
|
);
|
|
301
307
|
}
|
|
302
|
-
function
|
|
303
|
-
return
|
|
308
|
+
function deployWorkflowTemplateName(executionBoundary = "direct") {
|
|
309
|
+
return executionBoundary === "managed" ? "deploy.managed.workflow.yml" : "deploy.workflow.yml";
|
|
310
|
+
}
|
|
311
|
+
function renderDeployWorkflow({ workingDirectory, executionBoundary = "direct" }) {
|
|
312
|
+
return renderWorkflowTemplate(deployWorkflowTemplateName(executionBoundary), { workingDirectory });
|
|
304
313
|
}
|
|
305
314
|
function renderHostedProjectWorkflow({ workingDirectory }) {
|
|
306
315
|
return renderWorkflowTemplate("hosted-project.workflow.yml", { workingDirectory });
|
|
@@ -318,10 +327,13 @@ function ensureWorkflowFile(tenantRoot, fileName, expected) {
|
|
|
318
327
|
function ensureDeployWorkflow(tenantRoot) {
|
|
319
328
|
const repositoryRoot = resolveGitRepositoryRoot(tenantRoot);
|
|
320
329
|
const workingDirectory = relative(repositoryRoot, tenantRoot).replaceAll("\\", "/") || ".";
|
|
321
|
-
const
|
|
330
|
+
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
331
|
+
const executionBoundary = usesManagedHostOperationRequests(deployConfig) ? "managed" : "direct";
|
|
332
|
+
const expected = renderDeployWorkflow({ workingDirectory, executionBoundary });
|
|
322
333
|
return {
|
|
323
334
|
...ensureWorkflowFile(tenantRoot, "deploy.yml", expected),
|
|
324
|
-
workingDirectory
|
|
335
|
+
workingDirectory,
|
|
336
|
+
executionBoundary
|
|
325
337
|
};
|
|
326
338
|
}
|
|
327
339
|
function ensureHostedProjectWorkflow(tenantRoot) {
|
|
@@ -370,7 +382,7 @@ function nonEmptyValues(values = {}) {
|
|
|
370
382
|
Object.entries(values).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
371
383
|
);
|
|
372
384
|
}
|
|
373
|
-
async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save", valuesOverlay = {} } = {}) {
|
|
385
|
+
async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save", valuesOverlay = {}, managedHostMode = "auto" } = {}) {
|
|
374
386
|
const repository = maybeResolveGitHubRepositorySlug(tenantRoot);
|
|
375
387
|
if (!repository) {
|
|
376
388
|
if (dryRun) {
|
|
@@ -383,7 +395,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
|
|
|
383
395
|
}
|
|
384
396
|
throw new Error("Unable to determine GitHub repository from the current tenant. Configure an origin remote before syncing GitHub secrets.");
|
|
385
397
|
}
|
|
386
|
-
const required = requiredGitHubEnvironment(tenantRoot, { scope, purpose });
|
|
398
|
+
const required = requiredGitHubEnvironment(tenantRoot, { scope, purpose, managedHostMode });
|
|
387
399
|
const requiredSecrets = required.secrets;
|
|
388
400
|
const requiredVariables = required.variables;
|
|
389
401
|
const client = createGitHubApiClient();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type TreeseedEnvironmentScope } from '../../platform/environment.ts';
|
|
2
|
+
import type { TreeseedReconcileTarget } from '../../reconcile/contracts.ts';
|
|
3
|
+
export type TreeseedHostingAuditEnvironment = 'current' | 'local' | 'staging' | 'prod';
|
|
4
|
+
export type TreeseedHostingAuditResolvedEnvironment = 'local' | 'staging' | 'prod' | 'preview';
|
|
5
|
+
export type TreeseedHostingAuditHostKind = 'repository' | 'web' | 'processing' | 'email';
|
|
6
|
+
export type TreeseedHostingAuditCheckStatus = 'passed' | 'warning' | 'failed' | 'skipped' | 'repaired';
|
|
7
|
+
export type TreeseedHostingAuditSeverity = 'info' | 'warning' | 'critical';
|
|
8
|
+
export type TreeseedHostingAuditCheck = {
|
|
9
|
+
id: string;
|
|
10
|
+
hostType: TreeseedHostingAuditHostKind | 'platform';
|
|
11
|
+
provider: string;
|
|
12
|
+
category: 'config' | 'identity' | 'resource' | 'connectivity' | 'repair' | 'security';
|
|
13
|
+
status: TreeseedHostingAuditCheckStatus;
|
|
14
|
+
severity: TreeseedHostingAuditSeverity;
|
|
15
|
+
summary: string;
|
|
16
|
+
detail?: string;
|
|
17
|
+
resourceRef?: string;
|
|
18
|
+
repairAvailable?: boolean;
|
|
19
|
+
repaired?: boolean;
|
|
20
|
+
remediation?: string;
|
|
21
|
+
};
|
|
22
|
+
export type TreeseedHostingAuditReport = {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
environment: TreeseedHostingAuditResolvedEnvironment;
|
|
25
|
+
requestedEnvironment: TreeseedHostingAuditEnvironment;
|
|
26
|
+
repairMode: boolean;
|
|
27
|
+
repaired: boolean;
|
|
28
|
+
target: {
|
|
29
|
+
kind: TreeseedReconcileTarget['kind'];
|
|
30
|
+
scope?: string;
|
|
31
|
+
branchName?: string;
|
|
32
|
+
label: string;
|
|
33
|
+
};
|
|
34
|
+
hostKinds: TreeseedHostingAuditHostKind[];
|
|
35
|
+
checkedAt: string;
|
|
36
|
+
checks: TreeseedHostingAuditCheck[];
|
|
37
|
+
missingConfig: Array<{
|
|
38
|
+
key: string;
|
|
39
|
+
hostType: TreeseedHostingAuditHostKind | 'platform';
|
|
40
|
+
severity: TreeseedHostingAuditSeverity;
|
|
41
|
+
summary: string;
|
|
42
|
+
}>;
|
|
43
|
+
resources: Record<string, unknown>;
|
|
44
|
+
warnings: string[];
|
|
45
|
+
blockers: string[];
|
|
46
|
+
nextActions: string[];
|
|
47
|
+
};
|
|
48
|
+
export type TreeseedHostingAuditOptions = {
|
|
49
|
+
tenantRoot: string;
|
|
50
|
+
environment?: TreeseedHostingAuditEnvironment;
|
|
51
|
+
repair?: boolean;
|
|
52
|
+
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
53
|
+
valuesOverlay?: Record<string, string | undefined>;
|
|
54
|
+
hostKinds?: TreeseedHostingAuditHostKind[];
|
|
55
|
+
write?: (line: string) => void;
|
|
56
|
+
};
|
|
57
|
+
export declare function resolveTreeseedHostingAuditTarget({ tenantRoot, environment, }: {
|
|
58
|
+
tenantRoot: string;
|
|
59
|
+
environment?: TreeseedHostingAuditEnvironment;
|
|
60
|
+
}): {
|
|
61
|
+
environment: TreeseedHostingAuditResolvedEnvironment;
|
|
62
|
+
scope: TreeseedEnvironmentScope;
|
|
63
|
+
target: TreeseedReconcileTarget;
|
|
64
|
+
branchName: string | null;
|
|
65
|
+
};
|
|
66
|
+
export declare function runTreeseedHostingAudit({ tenantRoot, environment, repair, env, valuesOverlay, hostKinds: requestedHostKinds, write, }: TreeseedHostingAuditOptions): Promise<TreeseedHostingAuditReport>;
|
|
67
|
+
export declare function formatTreeseedHostingAuditReport(report: TreeseedHostingAuditReport): string;
|