@treeseed/sdk 0.6.4 → 0.6.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.
@@ -1452,7 +1452,20 @@ function resolveTreeseedLaunchEnvironment({
1452
1452
  throw error;
1453
1453
  }
1454
1454
  }
1455
- const scopedValues = scope === "local" ? { ...baseEnv, ...machineValues } : { ...machineValues, ...baseEnv };
1455
+ const registry = collectTreeseedEnvironmentContext(tenantRoot);
1456
+ const seedValues = scope === "local" ? { ...baseEnv, ...machineValues } : { ...machineValues, ...baseEnv };
1457
+ const suggestedValues = getTreeseedEnvironmentSuggestedValues({
1458
+ scope,
1459
+ purpose: "deploy",
1460
+ deployConfig: registry.context.deployConfig,
1461
+ tenantConfig: registry.context.tenantConfig,
1462
+ plugins: registry.context.plugins,
1463
+ values: seedValues
1464
+ });
1465
+ const nonSecretSuggestedValues = Object.fromEntries(
1466
+ registry.entries.filter((entry) => entry.sensitivity !== "secret" && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0).map((entry) => [entry.id, suggestedValues[entry.id]])
1467
+ );
1468
+ const scopedValues = scope === "local" ? { ...nonSecretSuggestedValues, ...baseEnv, ...machineValues } : { ...nonSecretSuggestedValues, ...machineValues, ...baseEnv };
1456
1469
  return {
1457
1470
  ...scopedValues,
1458
1471
  ...overrides
@@ -305,6 +305,7 @@ function requiredGitHubSecrets(tenantRoot) {
305
305
  function renderTenantWorkflowActionCommand() {
306
306
  return [
307
307
  "EXTRA_ARGS=()",
308
+ 'if [[ "${TREESEED_WORKFLOW_SKIP_PROVISION:-}" == "1" ]]; then EXTRA_ARGS+=(--skip-provision); fi',
308
309
  'if [[ -n "${TREESEED_WORKFLOW_PROJECT:-}" ]]; then EXTRA_ARGS+=(--project-id "${TREESEED_WORKFLOW_PROJECT}"); fi',
309
310
  'if [[ -n "${TREESEED_WORKFLOW_PREVIEW_ID:-}" ]]; then EXTRA_ARGS+=(--preview-id "${TREESEED_WORKFLOW_PREVIEW_ID}"); fi',
310
311
  "if test -f ./packages/sdk/scripts/tenant-workflow-action.ts; then",
@@ -14,7 +14,7 @@ import {
14
14
  import { createPublishedContentPipeline } from "../../platform/published-content-pipeline.js";
15
15
  import { collectTreeseedReconcileStatus, reconcileTreeseedTarget, resolveTreeseedBootstrapSelection } from "../../reconcile/index.js";
16
16
  import { loadTreeseedManifest } from "../../platform/tenant-config.js";
17
- import { applyTreeseedEnvironmentToProcess } from "./config-runtime.js";
17
+ import { applyTreeseedEnvironmentToProcess, assertTreeseedCommandEnvironment } from "./config-runtime.js";
18
18
  import {
19
19
  assertDeploymentInitialized,
20
20
  createPersistentDeployTarget,
@@ -115,6 +115,19 @@ function resolveProjectPlatformBootstrapSystems(options, siteConfig = loadCliDep
115
115
  }
116
116
  return selection.runnable.filter((system) => system !== "github");
117
117
  }
118
+ function runTenantPublishContentPreflight(options) {
119
+ const target = createPersistentDeployTarget(options.scope === "local" ? "staging" : options.scope);
120
+ applyTreeseedEnvironmentToProcess({ tenantRoot: options.tenantRoot, scope: options.scope, override: true });
121
+ if (options.scope !== "local") {
122
+ assertTreeseedCommandEnvironment({
123
+ tenantRoot: options.tenantRoot,
124
+ scope: options.scope,
125
+ purpose: "deploy"
126
+ });
127
+ assertDeploymentInitialized(options.tenantRoot, { target });
128
+ }
129
+ return target;
130
+ }
118
131
  function runWrangler(tenantRoot, args, extraEnv = {}, options = {}) {
119
132
  const result = spawnSync(process.execPath, [resolveWranglerBin(), ...args], {
120
133
  cwd: tenantRoot,
@@ -630,6 +643,7 @@ function probeScaleConfiguration(siteConfig, state) {
630
643
  };
631
644
  }
632
645
  async function publishContent(options, reporter) {
646
+ const target = runTenantPublishContentPreflight(options);
633
647
  const siteConfig = loadCliDeployConfig(options.tenantRoot);
634
648
  const tenantConfig = loadTreeseedManifest(resolve(options.tenantRoot, "src", "manifest.yaml"));
635
649
  const teamId = String(process.env.TREESEED_HOSTING_TEAM_ID ?? siteConfig.hosting?.teamId ?? siteConfig.slug).trim() || siteConfig.slug;
@@ -638,7 +652,6 @@ async function publishContent(options, reporter) {
638
652
  const branchName = currentRef(options.tenantRoot);
639
653
  const previewId = options.previewId ?? `staging-${sanitizeSegment(branchName, "preview")}-${sanitizeSegment(commitSha?.slice(0, 12), "latest")}`;
640
654
  const locator = resolveTeamScopedContentLocator(siteConfig, teamId);
641
- const target = createPersistentDeployTarget(options.scope === "local" ? "staging" : options.scope);
642
655
  const { wranglerPath } = ensureGeneratedWranglerConfig(options.tenantRoot, { target });
643
656
  const wranglerEnv = { CLOUDFLARE_ACCOUNT_ID: String(process.env.CLOUDFLARE_ACCOUNT_ID ?? siteConfig.cloudflare.accountId ?? "").trim() };
644
657
  const bucketName = String(process.env.TREESEED_CONTENT_BUCKET_NAME ?? siteConfig.cloudflare.r2?.bucketName ?? "").trim();
@@ -1141,7 +1154,7 @@ async function deployProjectPlatform(options) {
1141
1154
  scheduleVerification: railwayScheduleVerification
1142
1155
  });
1143
1156
  }
1144
- const monitor = await monitorProjectPlatform({ ...options, reporter });
1157
+ const monitor = await monitorProjectPlatform({ ...options, reporter, bootstrapSystems });
1145
1158
  await reportDeployment(reporter, {
1146
1159
  environment: options.scope,
1147
1160
  deploymentKind: "code",
@@ -1171,15 +1184,20 @@ async function monitorProjectPlatform(options) {
1171
1184
  const reporter = resolveReporter(options.tenantRoot, options.reporter);
1172
1185
  const target = createPersistentDeployTarget(options.scope === "local" ? "staging" : options.scope);
1173
1186
  const siteConfig = loadCliDeployConfig(options.tenantRoot);
1187
+ const selectedSystems = new Set(resolveProjectPlatformBootstrapSystems(options, siteConfig));
1188
+ const apiSelected = selectedSystems.has("api");
1189
+ const agentsSelected = selectedSystems.has("agents");
1174
1190
  const state = loadDeployState(options.tenantRoot, siteConfig, { target });
1175
1191
  const webProbeUrl = resolveImmediatePagesProbeUrl(siteConfig, state, target);
1176
1192
  const apiBaseUrl = resolveImmediateApiProbeUrl(siteConfig, state, target);
1193
+ const skippedApiCheck = apiSelected ? { ok: false, skipped: true, reason: "api_url_unconfigured" } : { ok: true, skipped: true, reason: "api_not_selected" };
1194
+ const skippedAgentCheck = agentsSelected ? { ok: false, skipped: true, reason: "api_url_unconfigured" } : { ok: true, skipped: true, reason: "agents_not_selected" };
1177
1195
  const checks = {
1178
1196
  pages: await probeHttp(webProbeUrl),
1179
- apiHealth: apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/healthz`) : { ok: false, skipped: true, reason: "api_url_unconfigured" },
1180
- apiReady: apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/readyz`) : { ok: false, skipped: true, reason: "api_url_unconfigured" },
1181
- d1Health: apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/healthz/deep`) : { ok: false, skipped: true, reason: "api_url_unconfigured" },
1182
- agentHealth: apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/internal/core/agent/healthz`) : { ok: false, skipped: true, reason: "api_url_unconfigured" },
1197
+ apiHealth: apiSelected && apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/healthz`) : skippedApiCheck,
1198
+ apiReady: apiSelected && apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/readyz`) : skippedApiCheck,
1199
+ d1Health: apiSelected && apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/healthz/deep`) : skippedApiCheck,
1200
+ agentHealth: agentsSelected && apiBaseUrl ? await probeHttp(`${String(apiBaseUrl).replace(/\/+$/u, "")}/internal/core/agent/healthz`) : skippedAgentCheck,
1183
1201
  r2: options.dryRun ? { ok: true, skipped: true, reason: "dry_run" } : probeR2(options.tenantRoot, siteConfig, state, target),
1184
1202
  queue: options.dryRun ? Promise.resolve({ ok: true, skipped: true, reason: "dry_run" }) : probeQueue(siteConfig, state),
1185
1203
  scaleProbe: probeScaleConfiguration(siteConfig, state),
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { pathToFileURL } from 'node:url';
2
3
  import { resolveScope, runProjectPlatformAction, } from '../operations/services/project-platform.js';
3
4
  const tenantRoot = process.cwd();
4
5
  function parseArgs(argv) {
@@ -8,6 +9,7 @@ function parseArgs(argv) {
8
9
  projectId: null,
9
10
  previewId: null,
10
11
  dryRun: false,
12
+ skipProvision: false,
11
13
  };
12
14
  const rest = [...argv];
13
15
  while (rest.length) {
@@ -50,6 +52,10 @@ function parseArgs(argv) {
50
52
  parsed.dryRun = true;
51
53
  continue;
52
54
  }
55
+ if (current === '--skip-provision') {
56
+ parsed.skipProvision = true;
57
+ continue;
58
+ }
53
59
  throw new Error(`Unknown workflow action argument: ${current}`);
54
60
  }
55
61
  return parsed;
@@ -63,9 +69,13 @@ async function main() {
63
69
  projectId: options.projectId ?? process.env.TREESEED_PROJECT_ID ?? null,
64
70
  previewId: options.previewId,
65
71
  dryRun: options.dryRun,
72
+ skipProvision: options.skipProvision,
66
73
  });
67
74
  if (result !== undefined) {
68
75
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
69
76
  }
70
77
  }
71
- await main();
78
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
79
+ await main();
80
+ }
81
+ export { parseArgs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -98,9 +98,12 @@ jobs:
98
98
  while IFS= read -r path; do
99
99
  [[ -z "${path}" ]] && continue
100
100
  case "${path}" in
101
- src/content/*|content/*|books/*|docs/*|migrations/*)
101
+ src/content/*|content/*|books/*|docs/*)
102
102
  content_changed="true"
103
103
  ;;
104
+ migrations/*)
105
+ code_changed="true"
106
+ ;;
104
107
  *)
105
108
  code_changed="true"
106
109
  ;;
@@ -131,6 +134,8 @@ jobs:
131
134
  needs.classify.outputs.release_tag == 'true'
132
135
  permissions:
133
136
  contents: read
137
+ env:
138
+ TREESEED_BOOTSTRAP_MODE: auto
134
139
  steps:
135
140
  - name: Checkout
136
141
  uses: actions/checkout@v4
@@ -178,6 +183,7 @@ __WORKING_DIRECTORY_BLOCK__
178
183
  deployments: write
179
184
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
180
185
  env:
186
+ TREESEED_BOOTSTRAP_MODE: auto
181
187
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
182
188
  CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
183
189
  TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME: ${{ vars.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME }}
@@ -195,7 +201,31 @@ __WORKING_DIRECTORY_BLOCK__
195
201
  TREESEED_SMTP_FROM: ${{ vars.TREESEED_SMTP_FROM }}
196
202
  TREESEED_SMTP_REPLY_TO: ${{ vars.TREESEED_SMTP_REPLY_TO }}
197
203
  RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
204
+ TREESEED_RAILWAY_WORKSPACE: ${{ vars.TREESEED_RAILWAY_WORKSPACE }}
205
+ TREESEED_HOSTING_KIND: ${{ vars.TREESEED_HOSTING_KIND }}
206
+ TREESEED_HOSTING_REGISTRATION: ${{ vars.TREESEED_HOSTING_REGISTRATION }}
207
+ TREESEED_HOSTING_TEAM_ID: ${{ vars.TREESEED_HOSTING_TEAM_ID }}
198
208
  TREESEED_MARKET_API_BASE_URL: ${{ vars.TREESEED_MARKET_API_BASE_URL }}
209
+ TREESEED_PROJECT_DOMAINS: ${{ vars.TREESEED_PROJECT_DOMAINS }}
210
+ TREESEED_API_BASE_URL: ${{ vars.TREESEED_API_BASE_URL }}
211
+ TREESEED_BETTER_AUTH_SECRET: ${{ secrets.TREESEED_BETTER_AUTH_SECRET }}
212
+ TREESEED_WEB_SERVICE_ID: ${{ vars.TREESEED_WEB_SERVICE_ID }}
213
+ TREESEED_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_WEB_SERVICE_SECRET }}
214
+ TREESEED_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_WEB_ASSERTION_SECRET }}
215
+ TREESEED_WEB_CSRF_SECRET: ${{ secrets.TREESEED_WEB_CSRF_SECRET }}
216
+ TREESEED_AUTH_GITHUB_CLIENT_ID: ${{ vars.TREESEED_AUTH_GITHUB_CLIENT_ID }}
217
+ TREESEED_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GITHUB_CLIENT_SECRET }}
218
+ TREESEED_AUTH_GOOGLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_GOOGLE_CLIENT_ID }}
219
+ TREESEED_AUTH_GOOGLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GOOGLE_CLIENT_SECRET }}
220
+ TREESEED_AUTH_MICROSOFT_CLIENT_ID: ${{ vars.TREESEED_AUTH_MICROSOFT_CLIENT_ID }}
221
+ TREESEED_AUTH_MICROSOFT_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_MICROSOFT_CLIENT_SECRET }}
222
+ TREESEED_AUTH_APPLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_APPLE_CLIENT_ID }}
223
+ TREESEED_AUTH_APPLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_APPLE_CLIENT_SECRET }}
224
+ TREESEED_API_D1_DATABASE_ID: ${{ vars.TREESEED_API_D1_DATABASE_ID }}
225
+ TREESEED_API_WEB_SERVICE_ID: ${{ vars.TREESEED_API_WEB_SERVICE_ID }}
226
+ TREESEED_API_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_API_WEB_SERVICE_SECRET }}
227
+ TREESEED_API_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_API_WEB_ASSERTION_SECRET }}
228
+ TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST: ${{ vars.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST }}
199
229
  TREESEED_PROJECT_ID: ${{ inputs.project_id || vars.TREESEED_PROJECT_ID }}
200
230
  TREESEED_PROJECT_RUNNER_TOKEN: ${{ secrets.TREESEED_PROJECT_RUNNER_TOKEN }}
201
231
  TREESEED_WORKER_POOL_SCALER: ${{ vars.TREESEED_WORKER_POOL_SCALER }}
@@ -235,6 +265,16 @@ __WORKING_DIRECTORY_BLOCK__
235
265
  shell: bash
236
266
  run: |
237
267
  __TENANT_WORKFLOW_ACTION_COMMAND_BLOCK__
268
+
269
+ - name: Upload Treeseed deployment state
270
+ if: always()
271
+ uses: actions/upload-artifact@v4
272
+ with:
273
+ name: treeseed-deploy-state-${{ needs.classify.outputs.scope }}
274
+ path: .treeseed/state
275
+ if-no-files-found: error
276
+ retention-days: 1
277
+ include-hidden-files: true
238
278
  __WORKING_DIRECTORY_BLOCK__
239
279
 
240
280
  deploy-code:
@@ -252,6 +292,7 @@ __WORKING_DIRECTORY_BLOCK__
252
292
  needs.classify.outputs.release_tag == 'true'
253
293
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
254
294
  env:
295
+ TREESEED_BOOTSTRAP_MODE: auto
255
296
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
256
297
  CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
257
298
  TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME: ${{ vars.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME }}
@@ -269,7 +310,31 @@ __WORKING_DIRECTORY_BLOCK__
269
310
  TREESEED_SMTP_FROM: ${{ vars.TREESEED_SMTP_FROM }}
270
311
  TREESEED_SMTP_REPLY_TO: ${{ vars.TREESEED_SMTP_REPLY_TO }}
271
312
  RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
313
+ TREESEED_RAILWAY_WORKSPACE: ${{ vars.TREESEED_RAILWAY_WORKSPACE }}
314
+ TREESEED_HOSTING_KIND: ${{ vars.TREESEED_HOSTING_KIND }}
315
+ TREESEED_HOSTING_REGISTRATION: ${{ vars.TREESEED_HOSTING_REGISTRATION }}
316
+ TREESEED_HOSTING_TEAM_ID: ${{ vars.TREESEED_HOSTING_TEAM_ID }}
272
317
  TREESEED_MARKET_API_BASE_URL: ${{ vars.TREESEED_MARKET_API_BASE_URL }}
318
+ TREESEED_PROJECT_DOMAINS: ${{ vars.TREESEED_PROJECT_DOMAINS }}
319
+ TREESEED_API_BASE_URL: ${{ vars.TREESEED_API_BASE_URL }}
320
+ TREESEED_BETTER_AUTH_SECRET: ${{ secrets.TREESEED_BETTER_AUTH_SECRET }}
321
+ TREESEED_WEB_SERVICE_ID: ${{ vars.TREESEED_WEB_SERVICE_ID }}
322
+ TREESEED_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_WEB_SERVICE_SECRET }}
323
+ TREESEED_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_WEB_ASSERTION_SECRET }}
324
+ TREESEED_WEB_CSRF_SECRET: ${{ secrets.TREESEED_WEB_CSRF_SECRET }}
325
+ TREESEED_AUTH_GITHUB_CLIENT_ID: ${{ vars.TREESEED_AUTH_GITHUB_CLIENT_ID }}
326
+ TREESEED_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GITHUB_CLIENT_SECRET }}
327
+ TREESEED_AUTH_GOOGLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_GOOGLE_CLIENT_ID }}
328
+ TREESEED_AUTH_GOOGLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GOOGLE_CLIENT_SECRET }}
329
+ TREESEED_AUTH_MICROSOFT_CLIENT_ID: ${{ vars.TREESEED_AUTH_MICROSOFT_CLIENT_ID }}
330
+ TREESEED_AUTH_MICROSOFT_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_MICROSOFT_CLIENT_SECRET }}
331
+ TREESEED_AUTH_APPLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_APPLE_CLIENT_ID }}
332
+ TREESEED_AUTH_APPLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_APPLE_CLIENT_SECRET }}
333
+ TREESEED_API_D1_DATABASE_ID: ${{ vars.TREESEED_API_D1_DATABASE_ID }}
334
+ TREESEED_API_WEB_SERVICE_ID: ${{ vars.TREESEED_API_WEB_SERVICE_ID }}
335
+ TREESEED_API_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_API_WEB_SERVICE_SECRET }}
336
+ TREESEED_API_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_API_WEB_ASSERTION_SECRET }}
337
+ TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST: ${{ vars.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST }}
273
338
  TREESEED_PROJECT_ID: ${{ inputs.project_id || vars.TREESEED_PROJECT_ID }}
274
339
  TREESEED_PROJECT_RUNNER_TOKEN: ${{ secrets.TREESEED_PROJECT_RUNNER_TOKEN }}
275
340
  TREESEED_WORKER_POOL_SCALER: ${{ vars.TREESEED_WORKER_POOL_SCALER }}
@@ -290,6 +355,7 @@ __WORKING_DIRECTORY_BLOCK__
290
355
  TREESEED_CONTENT_SERVING_MODE: published_runtime
291
356
  TREESEED_WORKFLOW_ACTION: deploy_code
292
357
  TREESEED_WORKFLOW_ENVIRONMENT: ${{ needs.classify.outputs.scope }}
358
+ TREESEED_WORKFLOW_SKIP_PROVISION: "1"
293
359
  TREESEED_WORKFLOW_PROJECT: ${{ inputs.project_id || vars.TREESEED_PROJECT_ID }}
294
360
  TREESEED_WORKFLOW_PREVIEW_ID: ${{ inputs.preview_id || needs.classify.outputs.preview_id }}
295
361
  steps:
@@ -315,6 +381,12 @@ __WORKING_DIRECTORY_BLOCK__
315
381
  - name: Install dependencies
316
382
  run: npm ci
317
383
 
384
+ - name: Download Treeseed deployment state
385
+ uses: actions/download-artifact@v4
386
+ with:
387
+ name: treeseed-deploy-state-${{ needs.classify.outputs.scope }}
388
+ path: .treeseed/state
389
+
318
390
  - name: Deploy Treeseed platform
319
391
  shell: bash
320
392
  run: |
@@ -332,11 +404,14 @@ __WORKING_DIRECTORY_BLOCK__
332
404
  contents: read
333
405
  deployments: write
334
406
  if: |
407
+ always() &&
408
+ needs.provision.result == 'success' &&
335
409
  (needs.classify.outputs.workflow_action == 'publish_content' ||
336
410
  needs.classify.outputs.content_changed == 'true') &&
337
411
  (needs['deploy-code'].result == 'success' || needs['deploy-code'].result == 'skipped')
338
412
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
339
413
  env:
414
+ TREESEED_BOOTSTRAP_MODE: auto
340
415
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
341
416
  CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
342
417
  TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME: ${{ vars.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME }}
@@ -354,7 +429,31 @@ __WORKING_DIRECTORY_BLOCK__
354
429
  TREESEED_SMTP_FROM: ${{ vars.TREESEED_SMTP_FROM }}
355
430
  TREESEED_SMTP_REPLY_TO: ${{ vars.TREESEED_SMTP_REPLY_TO }}
356
431
  RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
432
+ TREESEED_RAILWAY_WORKSPACE: ${{ vars.TREESEED_RAILWAY_WORKSPACE }}
433
+ TREESEED_HOSTING_KIND: ${{ vars.TREESEED_HOSTING_KIND }}
434
+ TREESEED_HOSTING_REGISTRATION: ${{ vars.TREESEED_HOSTING_REGISTRATION }}
435
+ TREESEED_HOSTING_TEAM_ID: ${{ vars.TREESEED_HOSTING_TEAM_ID }}
357
436
  TREESEED_MARKET_API_BASE_URL: ${{ vars.TREESEED_MARKET_API_BASE_URL }}
437
+ TREESEED_PROJECT_DOMAINS: ${{ vars.TREESEED_PROJECT_DOMAINS }}
438
+ TREESEED_API_BASE_URL: ${{ vars.TREESEED_API_BASE_URL }}
439
+ TREESEED_BETTER_AUTH_SECRET: ${{ secrets.TREESEED_BETTER_AUTH_SECRET }}
440
+ TREESEED_WEB_SERVICE_ID: ${{ vars.TREESEED_WEB_SERVICE_ID }}
441
+ TREESEED_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_WEB_SERVICE_SECRET }}
442
+ TREESEED_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_WEB_ASSERTION_SECRET }}
443
+ TREESEED_WEB_CSRF_SECRET: ${{ secrets.TREESEED_WEB_CSRF_SECRET }}
444
+ TREESEED_AUTH_GITHUB_CLIENT_ID: ${{ vars.TREESEED_AUTH_GITHUB_CLIENT_ID }}
445
+ TREESEED_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GITHUB_CLIENT_SECRET }}
446
+ TREESEED_AUTH_GOOGLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_GOOGLE_CLIENT_ID }}
447
+ TREESEED_AUTH_GOOGLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GOOGLE_CLIENT_SECRET }}
448
+ TREESEED_AUTH_MICROSOFT_CLIENT_ID: ${{ vars.TREESEED_AUTH_MICROSOFT_CLIENT_ID }}
449
+ TREESEED_AUTH_MICROSOFT_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_MICROSOFT_CLIENT_SECRET }}
450
+ TREESEED_AUTH_APPLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_APPLE_CLIENT_ID }}
451
+ TREESEED_AUTH_APPLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_APPLE_CLIENT_SECRET }}
452
+ TREESEED_API_D1_DATABASE_ID: ${{ vars.TREESEED_API_D1_DATABASE_ID }}
453
+ TREESEED_API_WEB_SERVICE_ID: ${{ vars.TREESEED_API_WEB_SERVICE_ID }}
454
+ TREESEED_API_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_API_WEB_SERVICE_SECRET }}
455
+ TREESEED_API_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_API_WEB_ASSERTION_SECRET }}
456
+ TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST: ${{ vars.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST }}
358
457
  TREESEED_PROJECT_ID: ${{ inputs.project_id || vars.TREESEED_PROJECT_ID }}
359
458
  TREESEED_PROJECT_RUNNER_TOKEN: ${{ secrets.TREESEED_PROJECT_RUNNER_TOKEN }}
360
459
  TREESEED_WORKER_POOL_SCALER: ${{ vars.TREESEED_WORKER_POOL_SCALER }}
@@ -390,6 +489,12 @@ __WORKING_DIRECTORY_BLOCK__
390
489
  - name: Install dependencies
391
490
  run: npm ci
392
491
 
492
+ - name: Download Treeseed deployment state
493
+ uses: actions/download-artifact@v4
494
+ with:
495
+ name: treeseed-deploy-state-${{ needs.classify.outputs.scope }}
496
+ path: .treeseed/state
497
+
393
498
  - name: Publish Treeseed content
394
499
  shell: bash
395
500
  run: |
@@ -404,6 +509,8 @@ __WORKING_DIRECTORY_BLOCK__
404
509
  - deploy-code
405
510
  - publish-content
406
511
  if: |
512
+ always() &&
513
+ needs.provision.result == 'success' &&
407
514
  (needs.classify.outputs.workflow_action == 'monitor' ||
408
515
  needs.classify.outputs.code_changed == 'true' ||
409
516
  needs.classify.outputs.content_changed == 'true' ||
@@ -415,12 +522,37 @@ __WORKING_DIRECTORY_BLOCK__
415
522
  deployments: write
416
523
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
417
524
  env:
525
+ TREESEED_BOOTSTRAP_MODE: auto
418
526
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
419
527
  CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
420
528
  TREESEED_CONTENT_BUCKET_NAME: ${{ vars.TREESEED_CONTENT_BUCKET_NAME }}
421
529
  TREESEED_CONTENT_BUCKET_BINDING: ${{ vars.TREESEED_CONTENT_BUCKET_BINDING }}
422
530
  RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
531
+ TREESEED_RAILWAY_WORKSPACE: ${{ vars.TREESEED_RAILWAY_WORKSPACE }}
532
+ TREESEED_HOSTING_KIND: ${{ vars.TREESEED_HOSTING_KIND }}
533
+ TREESEED_HOSTING_REGISTRATION: ${{ vars.TREESEED_HOSTING_REGISTRATION }}
534
+ TREESEED_HOSTING_TEAM_ID: ${{ vars.TREESEED_HOSTING_TEAM_ID }}
423
535
  TREESEED_MARKET_API_BASE_URL: ${{ vars.TREESEED_MARKET_API_BASE_URL }}
536
+ TREESEED_PROJECT_DOMAINS: ${{ vars.TREESEED_PROJECT_DOMAINS }}
537
+ TREESEED_API_BASE_URL: ${{ vars.TREESEED_API_BASE_URL }}
538
+ TREESEED_BETTER_AUTH_SECRET: ${{ secrets.TREESEED_BETTER_AUTH_SECRET }}
539
+ TREESEED_WEB_SERVICE_ID: ${{ vars.TREESEED_WEB_SERVICE_ID }}
540
+ TREESEED_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_WEB_SERVICE_SECRET }}
541
+ TREESEED_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_WEB_ASSERTION_SECRET }}
542
+ TREESEED_WEB_CSRF_SECRET: ${{ secrets.TREESEED_WEB_CSRF_SECRET }}
543
+ TREESEED_AUTH_GITHUB_CLIENT_ID: ${{ vars.TREESEED_AUTH_GITHUB_CLIENT_ID }}
544
+ TREESEED_AUTH_GITHUB_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GITHUB_CLIENT_SECRET }}
545
+ TREESEED_AUTH_GOOGLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_GOOGLE_CLIENT_ID }}
546
+ TREESEED_AUTH_GOOGLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_GOOGLE_CLIENT_SECRET }}
547
+ TREESEED_AUTH_MICROSOFT_CLIENT_ID: ${{ vars.TREESEED_AUTH_MICROSOFT_CLIENT_ID }}
548
+ TREESEED_AUTH_MICROSOFT_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_MICROSOFT_CLIENT_SECRET }}
549
+ TREESEED_AUTH_APPLE_CLIENT_ID: ${{ vars.TREESEED_AUTH_APPLE_CLIENT_ID }}
550
+ TREESEED_AUTH_APPLE_CLIENT_SECRET: ${{ secrets.TREESEED_AUTH_APPLE_CLIENT_SECRET }}
551
+ TREESEED_API_D1_DATABASE_ID: ${{ vars.TREESEED_API_D1_DATABASE_ID }}
552
+ TREESEED_API_WEB_SERVICE_ID: ${{ vars.TREESEED_API_WEB_SERVICE_ID }}
553
+ TREESEED_API_WEB_SERVICE_SECRET: ${{ secrets.TREESEED_API_WEB_SERVICE_SECRET }}
554
+ TREESEED_API_WEB_ASSERTION_SECRET: ${{ secrets.TREESEED_API_WEB_ASSERTION_SECRET }}
555
+ TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST: ${{ vars.TREESEED_API_BOOTSTRAP_ADMIN_ALLOWLIST }}
424
556
  TREESEED_PROJECT_ID: ${{ inputs.project_id || vars.TREESEED_PROJECT_ID }}
425
557
  TREESEED_PROJECT_RUNNER_TOKEN: ${{ secrets.TREESEED_PROJECT_RUNNER_TOKEN }}
426
558
  TREESEED_WORKER_POOL_SCALER: ${{ vars.TREESEED_WORKER_POOL_SCALER }}
@@ -445,6 +577,12 @@ __WORKING_DIRECTORY_BLOCK__
445
577
  - name: Install dependencies
446
578
  run: npm ci
447
579
 
580
+ - name: Download Treeseed deployment state
581
+ uses: actions/download-artifact@v4
582
+ with:
583
+ name: treeseed-deploy-state-${{ needs.classify.outputs.scope }}
584
+ path: .treeseed/state
585
+
448
586
  - name: Report Treeseed deployment state
449
587
  shell: bash
450
588
  run: |