@smoothbricks/cli 0.10.7 → 0.10.9

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.
Files changed (58) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +24 -1
  3. package/dist/github-ci/index.d.ts +44 -4
  4. package/dist/github-ci/index.d.ts.map +1 -1
  5. package/dist/github-ci/index.js +220 -34
  6. package/dist/monorepo/ci-workflow.js +16 -6
  7. package/dist/monorepo/managed-files.d.ts.map +1 -1
  8. package/dist/monorepo/managed-files.js +19 -1
  9. package/dist/monorepo/pr-preview-cleanup-workflow.d.ts +5 -0
  10. package/dist/monorepo/pr-preview-cleanup-workflow.d.ts.map +1 -0
  11. package/dist/monorepo/pr-preview-cleanup-workflow.js +38 -0
  12. package/dist/monorepo/publish-workflow.js +3 -3
  13. package/dist/monorepo/tool-validation.d.ts.map +1 -1
  14. package/dist/monorepo/tool-validation.js +85 -5
  15. package/dist/playwright/index.d.ts +22 -0
  16. package/dist/playwright/index.d.ts.map +1 -0
  17. package/dist/playwright/index.js +44 -0
  18. package/dist/release/bootstrap-npm-packages.d.ts +3 -0
  19. package/dist/release/bootstrap-npm-packages.d.ts.map +1 -1
  20. package/dist/release/bootstrap-npm-packages.js +21 -0
  21. package/dist/release/index.d.ts +1 -0
  22. package/dist/release/index.d.ts.map +1 -1
  23. package/dist/release/index.js +31 -6
  24. package/dist/wrangler/cloudflare.d.ts +87 -0
  25. package/dist/wrangler/cloudflare.d.ts.map +1 -0
  26. package/dist/wrangler/cloudflare.js +238 -0
  27. package/dist/wrangler/deploy-environment.d.ts +48 -0
  28. package/dist/wrangler/deploy-environment.d.ts.map +1 -0
  29. package/dist/wrangler/deploy-environment.js +383 -0
  30. package/dist/wrangler/environment.d.ts +58 -0
  31. package/dist/wrangler/environment.d.ts.map +1 -0
  32. package/dist/wrangler/environment.js +297 -0
  33. package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +3 -4
  34. package/package.json +9 -2
  35. package/src/cli.ts +27 -3
  36. package/src/github-ci/index.test.ts +175 -2
  37. package/src/github-ci/index.ts +274 -31
  38. package/src/monorepo/__tests__/ci-workflow.test.ts +10 -4
  39. package/src/monorepo/__tests__/pr-preview-cleanup-workflow.test.ts +23 -0
  40. package/src/monorepo/__tests__/publish-workflow.test.ts +7 -5
  41. package/src/monorepo/ci-workflow.ts +16 -6
  42. package/src/monorepo/managed-files.test.ts +56 -1
  43. package/src/monorepo/managed-files.ts +20 -1
  44. package/src/monorepo/pr-preview-cleanup-workflow.ts +44 -0
  45. package/src/monorepo/publish-workflow.ts +8 -5
  46. package/src/monorepo/tool-validation.test.ts +85 -0
  47. package/src/monorepo/tool-validation.ts +94 -5
  48. package/src/playwright/index.test.ts +90 -0
  49. package/src/playwright/index.ts +73 -0
  50. package/src/release/__tests__/bootstrap-npm-packages.test.ts +63 -2
  51. package/src/release/bootstrap-npm-packages.ts +34 -0
  52. package/src/release/index.ts +30 -4
  53. package/src/wrangler/cloudflare.test.ts +76 -0
  54. package/src/wrangler/cloudflare.ts +292 -0
  55. package/src/wrangler/deploy-environment.test.ts +354 -0
  56. package/src/wrangler/deploy-environment.ts +445 -0
  57. package/src/wrangler/environment.test.ts +173 -0
  58. package/src/wrangler/environment.ts +366 -0
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAOA,wBAAsB,MAAM,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AASA,wBAAsB,MAAM,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE"}
package/dist/cli.js CHANGED
@@ -2,7 +2,9 @@ import { Command, CommanderError } from 'commander';
2
2
  import { variants } from './generate/index.js';
3
3
  import { cliPackageVersion } from './lib/cli-package.js';
4
4
  import { findRepoRoot } from './lib/run.js';
5
+ import { ensureChromium } from './playwright/index.js';
5
6
  import { resolvePrConflicts } from './pr/index.js';
7
+ import { cleanupPullRequest, deployEnvironment } from './wrangler/deploy-environment.js';
6
8
  import { scaffold } from './wrangler/scaffold.js';
7
9
  export async function runCli(argv = process.argv.slice(2)) {
8
10
  const program = buildProgram();
@@ -147,6 +149,7 @@ function buildProgram() {
147
149
  .requiredOption('--targets <targets>', 'comma-separated Nx platform target names or globs')
148
150
  .requiredOption('--output <path>', 'output directory for current and repair artifacts')
149
151
  .option('--ref <ref>', 'fixed release graph ref to inspect')
152
+ .option('--github-output <path>', 'append selected current platform projects to a GitHub Actions output file')
150
153
  .action(async (options) => {
151
154
  // The source self-hosting shim has no Typia transform; release commands import transformed output validators.
152
155
  const { releaseCollectPlatformOutputs } = await import('./release/index.js');
@@ -307,7 +310,7 @@ function buildProgram() {
307
310
  });
308
311
  githubCi
309
312
  .command('nx-deploy')
310
- .requiredOption('--configuration <configuration>')
313
+ .option('--environment <environment>', 'explicit staging, production, or prN override')
311
314
  .option('--mode <mode>', 'auto, affected, or run-many', 'run-many')
312
315
  .option('--name <name>')
313
316
  .option('--step <step>')
@@ -327,6 +330,14 @@ function buildProgram() {
327
330
  process.exitCode = exitCode;
328
331
  }
329
332
  });
333
+ const playwright = program.command('playwright').description('Manage Playwright browsers');
334
+ const playwrightEnsure = playwright.command('ensure').description('Ensure a Playwright browser is available');
335
+ playwrightEnsure
336
+ .command('chromium')
337
+ .description('Ensure Chromium is available for browser tests')
338
+ .action(async () => {
339
+ await ensureChromium();
340
+ });
330
341
  const wrangler = program.command('wrangler').description('Cloudflare wrangler project helpers');
331
342
  wrangler
332
343
  .command('scaffold <project>')
@@ -335,6 +346,18 @@ function buildProgram() {
335
346
  .action(async (project, options) => {
336
347
  scaffold(await findRepoRoot(), project, { force: options.force });
337
348
  });
349
+ wrangler
350
+ .command('deploy-environment')
351
+ .requiredOption('--environment <environment>', 'staging, production, or prN')
352
+ .action(async (options) => {
353
+ await deployEnvironment(process.cwd(), options.environment);
354
+ });
355
+ wrangler
356
+ .command('cleanup-pr')
357
+ .requiredOption('--pr <number>', 'pull-request number')
358
+ .action(async (options) => {
359
+ await cleanupPullRequest(process.cwd(), Number(options.pr));
360
+ });
338
361
  return program;
339
362
  }
340
363
  function booleanOption(value) {
@@ -1,5 +1,21 @@
1
1
  import { type ProjectTargets } from '../nx/index.js';
2
+ import { type EnvironmentToken } from '../wrangler/environment.js';
2
3
  import type { NxTargetRun } from './outputs.js';
4
+ export interface GithubActionsEventPayload {
5
+ action?: string;
6
+ repository?: {
7
+ default_branch?: string;
8
+ full_name?: string;
9
+ };
10
+ pull_request?: {
11
+ number?: number;
12
+ head?: {
13
+ repo?: {
14
+ full_name?: string;
15
+ };
16
+ };
17
+ };
18
+ }
3
19
  type NxSmartMode = 'auto' | 'affected' | 'run-many';
4
20
  export declare function cleanupGithubCiCache(root: string): Promise<void>;
5
21
  export declare function nxSmartArgs(target: string, mode: 'affected' | 'run-many', configuration?: string): string[];
@@ -26,14 +42,38 @@ export declare function expandNxTargetRuns(projects: ProjectTargets[], options:
26
42
  export declare function expandNxTargetDependencyRuns(runs: NxTargetRun[]): NxTargetRun[];
27
43
  export declare function nxRunManyArgs(run: NxTargetRun, configuration?: string): string[];
28
44
  export declare function readGitHeadSha(root: string): Promise<string>;
29
- export declare function githubCiNxRunMany(root: string, options: NxRunManyOptions): Promise<void>;
45
+ export declare function githubCiNxRunMany(root: string, options: NxRunManyOptions): Promise<ExpandedNxTargetRuns>;
30
46
  export declare function githubCiApplyOutputs(root: string, directories: string[], expectedSourceSha: string): Promise<void>;
31
- export declare function githubCiNxDeploy(root: string, options: {
32
- configuration: string;
47
+ export interface GithubCiNxDeployOptions {
48
+ environment?: string;
33
49
  mode?: NxSmartMode;
34
50
  name?: string;
35
51
  step?: string;
36
52
  verify?: boolean;
37
- }): Promise<void>;
53
+ }
54
+ export interface GithubCiNxDeployDependencies {
55
+ listProjects?: (root: string, target: string, mode: 'affected' | 'run-many') => Promise<string[]>;
56
+ runNx?: (args: string[], root: string) => Promise<number>;
57
+ appendSummary?: (summaryPath: string, content: string) => Promise<void>;
58
+ publishDeployment?: (environment: `pr${number}`, url: string) => Promise<void>;
59
+ setStatus?: (state: 'pending' | 'success' | 'failure') => Promise<void>;
60
+ processEnv?: NodeJS.ProcessEnv;
61
+ eventPayload?: GithubActionsEventPayload;
62
+ }
63
+ export declare function githubCiNxDeploy(root: string, options: GithubCiNxDeployOptions, dependencies?: GithubCiNxDeployDependencies): Promise<void>;
64
+ export declare function resolveDeploymentEnvironment(explicit: string | undefined, environment: NodeJS.ProcessEnv, event: GithubActionsEventPayload | undefined): EnvironmentToken;
65
+ export declare function selectEnvironmentDeployProjects(candidates: string[], loadProject: (project: string) => Promise<unknown>): Promise<string[]>;
66
+ export interface GithubApiProcessResult {
67
+ exitCode: number;
68
+ stdout: string;
69
+ stderr: string;
70
+ }
71
+ export interface GithubApiProcessRunner {
72
+ run(args: string[], input: string, cwd: string): Promise<GithubApiProcessResult>;
73
+ }
74
+ export declare class NodeGithubApiProcessRunner implements GithubApiProcessRunner {
75
+ run(args: string[], input: string, cwd: string): Promise<GithubApiProcessResult>;
76
+ }
77
+ export declare function publishGithubDeployment(environment: `pr${number}`, url: string, processEnvironment: NodeJS.ProcessEnv, runner?: GithubApiProcessRunner): Promise<void>;
38
78
  export {};
39
79
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAmBhD,KAAK,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAKpD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AAmFD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAO3G;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACpG,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,oBAAoB,CAmD9G;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAwC/E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAehF;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9F;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EAAE,EACrB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAGf;AA0CD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACrG,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/github-ci/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { spawn } from 'node:child_process';
1
2
  import { existsSync, readFileSync } from 'node:fs';
2
3
  import { appendFile, mkdtemp, realpath, rename, rm } from 'node:fs/promises';
3
4
  import { dirname, join } from 'node:path';
@@ -6,22 +7,35 @@ import typia from 'typia';
6
7
  import { parseStringArrayText } from '../lib/json.js';
7
8
  import { decode, run, runStatus } from '../lib/run.js';
8
9
  import { readProjectTargets } from '../nx/index.js';
10
+ import { isPullRequestEnvironment, parseEnvironmentToken, pullRequestEnvironment, } from '../wrangler/environment.js';
9
11
  const parseGithubActionsEvent = (() => {
10
- const _io0 = input => undefined === input.repository || "object" === typeof input.repository && null !== input.repository && false === Array.isArray(input.repository) && _io1(input.repository);
11
- const _io1 = input => undefined === input.default_branch || "string" === typeof input.default_branch;
12
+ const _ip0 = input => undefined === input["full_name"] || "string" === typeof input["full_name"];
13
+ const _io0 = input => (undefined === input.action || "string" === typeof input.action) && (undefined === input.repository || "object" === typeof input.repository && null !== input.repository && false === Array.isArray(input.repository) && _io1(input.repository)) && (undefined === input.pull_request || "object" === typeof input.pull_request && null !== input.pull_request && false === Array.isArray(input.pull_request) && _io2(input.pull_request));
14
+ const _io1 = input => (undefined === input.default_branch || "string" === typeof input.default_branch) && _ip0(input);
15
+ const _io2 = input => (undefined === input.number || "number" === typeof input.number) && (undefined === input.head || "object" === typeof input.head && null !== input.head && false === Array.isArray(input.head) && _io3(input.head));
16
+ const _io3 = input => undefined === input.repo || "object" === typeof input.repo && null !== input.repo && false === Array.isArray(input.repo) && _io4(input.repo);
17
+ const _io4 = input => _ip0(input);
12
18
  const __is = input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
13
19
  return input => { input = JSON.parse(input); return __is(input) ? input : null; };
14
20
  })();
15
- const parseNxProjectDeployConfigurations = (() => {
21
+ const isGithubDeployment = (() => {
22
+ const _io0 = input => "number" === typeof input.id;
23
+ return input => "object" === typeof input && null !== input && _io0(input);
24
+ })();
25
+ const isNxProjectDeployTarget = (() => {
26
+ const _ip0 = input => true;
16
27
  const _io0 = input => undefined === input.targets || "object" === typeof input.targets && null !== input.targets && false === Array.isArray(input.targets) && _io1(input.targets);
17
28
  const _io1 = input => undefined === input.deploy || "object" === typeof input.deploy && null !== input.deploy && false === Array.isArray(input.deploy) && _io2(input.deploy);
18
- const _io2 = input => undefined === input.configurations || "object" === typeof input.configurations && null !== input.configurations && false === Array.isArray(input.configurations) && _io3(input.configurations);
19
- const _io3 = input => Object.keys(input).every(key => {
20
- const value = input[key];
21
- if (undefined === value)
22
- return true;
23
- return true;
24
- });
29
+ const _io2 = input => _ip0(input) && (undefined === input.options || "object" === typeof input.options && null !== input.options && false === Array.isArray(input.options) && _io3(input.options));
30
+ const _io3 = input => _ip0(input);
31
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
32
+ })();
33
+ const parseNxProjectDeployTarget = (() => {
34
+ const _ip0 = input => true;
35
+ const _io0 = input => undefined === input.targets || "object" === typeof input.targets && null !== input.targets && false === Array.isArray(input.targets) && _io1(input.targets);
36
+ const _io1 = input => undefined === input.deploy || "object" === typeof input.deploy && null !== input.deploy && false === Array.isArray(input.deploy) && _io2(input.deploy);
37
+ const _io2 = input => _ip0(input) && (undefined === input.options || "object" === typeof input.options && null !== input.options && false === Array.isArray(input.options) && _io3(input.options));
38
+ const _io3 = input => _ip0(input);
25
39
  const __is = input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
26
40
  return input => { input = JSON.parse(input); return __is(input) ? input : null; };
27
41
  })();
@@ -262,6 +276,7 @@ export async function githubCiNxRunMany(root, options) {
262
276
  const sourceSha = await readGitHeadSha(root);
263
277
  await collectNxOutputs(root, options.collectOutputs, expandNxTargetDependencyRuns(expanded.runs), sourceSha);
264
278
  }
279
+ return expanded;
265
280
  }
266
281
  export async function githubCiApplyOutputs(root, directories, expectedSourceSha) {
267
282
  const { applyCollectedOutputs } = await loadOutputBoundary();
@@ -300,31 +315,74 @@ function commaSeparatedValues(value) {
300
315
  .map((entry) => entry.trim())
301
316
  .filter(Boolean);
302
317
  }
303
- export async function githubCiNxDeploy(root, options) {
304
- const name = options.name ?? `Deploy ${options.configuration}`;
318
+ export async function githubCiNxDeploy(root, options, dependencies = {}) {
319
+ const processEnv = dependencies.processEnv ?? process.env;
320
+ const eventPayload = dependencies.eventPayload ?? readGithubActionsEvent(processEnv);
321
+ const environment = resolveDeploymentEnvironment(options.environment, processEnv, eventPayload);
322
+ const name = options.name ?? 'Deploy Environment';
305
323
  const step = options.step ?? '';
306
- await createGithubStatus(name, step);
324
+ const setStatus = dependencies.setStatus ??
325
+ ((state) => state === 'pending' ? createGithubStatus(name, step) : updateGithubStatus(name, state, step));
326
+ await setStatus('pending');
307
327
  const mode = resolveNxSmartMode(options.mode ?? 'run-many');
308
- const projects = await deployProjectsWithConfiguration(root, options.configuration, mode);
328
+ const listProjects = dependencies.listProjects ?? listNxProjectsWithTarget;
329
+ const runNx = dependencies.runNx ?? ((args, commandRoot) => runStatus('nx', args, commandRoot));
330
+ const projects = await listProjects(root, 'deploy', mode);
309
331
  if (projects.length === 0) {
310
- console.log(`No ${mode} deploy projects with configuration ${options.configuration}; skipping.`);
311
- await updateGithubStatus(name, 'success', step);
332
+ console.log(`No ${mode} deploy projects; skipping ${environment}.`);
333
+ await setStatus('success');
312
334
  return;
313
335
  }
314
336
  const projectList = projects.join(',');
315
337
  const targets = options.verify === true ? ['build', 'lint', 'test', 'deploy'] : ['deploy'];
316
338
  for (const target of targets) {
317
- const nxArgs = ['run-many', '-t', target, `--projects=${projectList}`, `--parallel=${NX_PARALLEL}`];
339
+ const nxArgs = [
340
+ 'run-many',
341
+ '-t',
342
+ target,
343
+ `--projects=${projectList}`,
344
+ '--exclude=tag:permanent-deploy-target',
345
+ `--parallel=${NX_PARALLEL}`,
346
+ ];
318
347
  if (target === 'deploy') {
319
- nxArgs.push(`--configuration=${options.configuration}`);
348
+ nxArgs.push(`--environment=${environment}`);
320
349
  }
321
- const status = await runStatus('nx', nxArgs, root);
350
+ const status = await runNx(nxArgs, root);
322
351
  if (status !== 0) {
323
- await updateGithubStatus(name, 'failure', step);
352
+ await setStatus('failure');
324
353
  throw new Error(`nx ${nxArgs.join(' ')} failed with exit code ${status}`);
325
354
  }
326
355
  }
327
- await updateGithubStatus(name, 'success', step);
356
+ if (isPullRequestEnvironment(environment)) {
357
+ const url = `https://app.${environment}.conloca.com`;
358
+ const summaryPath = processEnv.GITHUB_STEP_SUMMARY;
359
+ if (summaryPath) {
360
+ const appendSummary = dependencies.appendSummary ?? appendFile;
361
+ await appendSummary(summaryPath, `## ${environment} deployment\n\n[View deployment](${url})\n`);
362
+ }
363
+ const publishDeployment = dependencies.publishDeployment ??
364
+ ((token, deploymentUrl) => publishGithubDeployment(token, deploymentUrl, processEnv));
365
+ await publishDeployment(environment, url);
366
+ }
367
+ if (environment !== 'production') {
368
+ const e2eProjects = await listProjects(root, 'e2e-deployed', 'run-many');
369
+ if (e2eProjects.length > 0) {
370
+ const nxArgs = [
371
+ 'run-many',
372
+ '-t',
373
+ 'e2e-deployed',
374
+ `--projects=${e2eProjects.join(',')}`,
375
+ `--parallel=${NX_PARALLEL}`,
376
+ `--environment=${environment}`,
377
+ ];
378
+ const status = await runNx(nxArgs, root);
379
+ if (status !== 0) {
380
+ await setStatus('failure');
381
+ throw new Error(`nx ${nxArgs.join(' ')} failed with exit code ${status}`);
382
+ }
383
+ }
384
+ }
385
+ await setStatus('success');
328
386
  }
329
387
  function resolveNxSmartMode(mode) {
330
388
  if (mode === 'affected' || mode === 'run-many') {
@@ -357,24 +415,152 @@ function eventDefaultBranch() {
357
415
  return undefined;
358
416
  }
359
417
  }
360
- async function deployProjectsWithConfiguration(root, configuration, mode) {
361
- const listArgs = mode === 'affected'
362
- ? ['show', 'projects', '--affected', '--withTarget', 'deploy', '--json']
363
- : ['show', 'projects', '--withTarget', 'deploy', '--json'];
418
+ export function resolveDeploymentEnvironment(explicit, environment, event) {
419
+ if (explicit !== undefined)
420
+ return parseEnvironmentToken(explicit);
421
+ if (environment.GITHUB_EVENT_NAME === 'pull_request') {
422
+ if (!event?.action || !['opened', 'reopened', 'synchronize'].includes(event.action)) {
423
+ throw new Error('Pull-request deployment runs only for opened, reopened, or synchronize events.');
424
+ }
425
+ const repository = event.repository?.full_name;
426
+ const headRepository = event.pull_request?.head?.repo?.full_name;
427
+ if (!repository || !headRepository || repository !== headRepository) {
428
+ throw new Error('Pull-request deployment is restricted to same-repository pull requests.');
429
+ }
430
+ const number = event.pull_request?.number;
431
+ if (number === undefined)
432
+ throw new Error('GitHub pull_request event is missing its PR number.');
433
+ return pullRequestEnvironment(number);
434
+ }
435
+ if (environment.GITHUB_EVENT_NAME === 'push' && environment.GITHUB_REF_NAME === 'private') {
436
+ return 'staging';
437
+ }
438
+ if (environment.GITHUB_EVENT_NAME === 'release') {
439
+ return 'production';
440
+ }
441
+ throw new Error('Cannot resolve a deployment environment from this GitHub event; pass --environment explicitly.');
442
+ }
443
+ function readGithubActionsEvent(environment) {
444
+ const eventPath = environment.GITHUB_EVENT_PATH;
445
+ if (!eventPath)
446
+ return undefined;
447
+ try {
448
+ return parseGithubActionsEvent(readFileSync(eventPath, 'utf8')) || undefined;
449
+ }
450
+ catch {
451
+ return undefined;
452
+ }
453
+ }
454
+ async function listNxProjectsWithTarget(root, target, mode) {
455
+ const listArgs = ['show', 'projects'];
456
+ if (mode === 'affected')
457
+ listArgs.push('--affected');
458
+ listArgs.push('--withTarget', target);
459
+ if (target === 'deploy')
460
+ listArgs.push('--exclude=tag:permanent-deploy-target');
461
+ listArgs.push('--json');
364
462
  const result = await $ `nx ${listArgs}`.cwd(root).quiet();
365
- const candidates = nxProjectList(decode(result.stdout));
366
- const projects = [];
463
+ const candidates = nxProjectList(decode(result.stdout)).sort((left, right) => left.localeCompare(right));
464
+ if (target !== 'deploy')
465
+ return candidates;
466
+ return selectEnvironmentDeployProjects(candidates, async (project) => {
467
+ const projectResult = await $ `nx show project ${project} --json`.cwd(root).quiet();
468
+ const parsed = parseNxProjectDeployTarget(decode(projectResult.stdout));
469
+ if (!parsed)
470
+ throw new Error(`nx show project ${project} returned invalid JSON.`);
471
+ return parsed;
472
+ });
473
+ }
474
+ export async function selectEnvironmentDeployProjects(candidates, loadProject) {
475
+ const selected = [];
367
476
  for (const project of candidates) {
368
- if (await deployTargetHasConfiguration(root, project, configuration)) {
369
- projects.push(project);
477
+ const definition = await loadProject(project);
478
+ if (!isNxProjectDeployTarget(definition))
479
+ continue;
480
+ const deploy = definition.targets?.deploy;
481
+ const commandValue = deploy?.options?.command ?? deploy?.command;
482
+ if (typeof commandValue === 'string' && commandValue.includes('smoo wrangler deploy-environment')) {
483
+ selected.push(project);
370
484
  }
371
485
  }
372
- return projects.sort((a, b) => a.localeCompare(b));
486
+ return selected;
487
+ }
488
+ export class NodeGithubApiProcessRunner {
489
+ run(args, input, cwd) {
490
+ const { promise, resolve, reject } = Promise.withResolvers();
491
+ const child = spawn('gh', args, { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
492
+ let stdout = '';
493
+ let stderr = '';
494
+ child.stdout.setEncoding('utf8');
495
+ child.stderr.setEncoding('utf8');
496
+ child.stdout.on('data', (chunk) => {
497
+ stdout += chunk;
498
+ });
499
+ child.stderr.on('data', (chunk) => {
500
+ stderr += chunk;
501
+ });
502
+ child.once('error', reject);
503
+ child.once('close', (code) => {
504
+ resolve({ exitCode: code ?? -1, stdout, stderr });
505
+ });
506
+ child.stdin.end(input);
507
+ return promise;
508
+ }
373
509
  }
374
- async function deployTargetHasConfiguration(root, project, configuration) {
375
- const result = await $ `nx show project ${project} --json`.cwd(root).quiet();
376
- const parsed = parseNxProjectDeployConfigurations(decode(result.stdout));
377
- return parsed?.targets?.deploy?.configurations?.[configuration] !== undefined;
510
+ export async function publishGithubDeployment(environment, url, processEnvironment, runner = new NodeGithubApiProcessRunner()) {
511
+ const repository = processEnvironment.GITHUB_REPOSITORY;
512
+ const sha = processEnvironment.GITHUB_SHA;
513
+ if (!repository || !sha)
514
+ throw new Error('GITHUB_REPOSITORY and GITHUB_SHA are required to publish a deployment.');
515
+ const createBody = JSON.stringify({
516
+ ref: sha,
517
+ environment,
518
+ auto_merge: false,
519
+ required_contexts: [],
520
+ transient_environment: true,
521
+ production_environment: false,
522
+ });
523
+ const createResult = await runner.run([
524
+ 'api',
525
+ '--method',
526
+ 'POST',
527
+ '-H',
528
+ 'Accept: application/vnd.github+json',
529
+ `/repos/${repository}/deployments`,
530
+ '--input',
531
+ '-',
532
+ ], createBody, process.cwd());
533
+ if (createResult.exitCode !== 0) {
534
+ throw new Error(`GitHub deployment creation failed with exit code ${createResult.exitCode}: ${createResult.stderr.trim()}`);
535
+ }
536
+ let deployment;
537
+ try {
538
+ deployment = JSON.parse(createResult.stdout);
539
+ }
540
+ catch {
541
+ throw new Error('GitHub returned invalid JSON while creating a deployment.');
542
+ }
543
+ if (!isGithubDeployment(deployment))
544
+ throw new Error('GitHub returned an invalid deployment response.');
545
+ const statusBody = JSON.stringify({
546
+ state: 'success',
547
+ environment,
548
+ environment_url: url,
549
+ auto_inactive: false,
550
+ });
551
+ const statusResult = await runner.run([
552
+ 'api',
553
+ '--method',
554
+ 'POST',
555
+ '-H',
556
+ 'Accept: application/vnd.github+json',
557
+ `/repos/${repository}/deployments/${deployment.id}/statuses`,
558
+ '--input',
559
+ '-',
560
+ ], statusBody, process.cwd());
561
+ if (statusResult.exitCode !== 0) {
562
+ throw new Error(`GitHub deployment status failed with exit code ${statusResult.exitCode}: ${statusResult.stderr.trim()}`);
563
+ }
378
564
  }
379
565
  function nxProjectList(output) {
380
566
  return parseStringArrayText(output) ?? [];
@@ -29,7 +29,7 @@ export function defineCiWorkflow(options) {
29
29
  { kind: CiWorkflowStepKind.ManagedFilesDispatch, name: '🔁 Dispatch managed-file drift healing' },
30
30
  ];
31
31
  if (options.deploy) {
32
- steps.push({ kind: CiWorkflowStepKind.Deploy, name: '🚀 Deploy Staging' });
32
+ steps.push({ kind: CiWorkflowStepKind.Deploy, name: '🚀 Deploy Environment' });
33
33
  }
34
34
  steps.push({ kind: CiWorkflowStepKind.SaveNxCache, name: '💾 Save Nx cache' }, { kind: CiWorkflowStepKind.UploadTraceDbs, name: '📎 Upload trace DBs' }, { kind: CiWorkflowStepKind.SaveNixDevenv, name: '🧹 Cleanup and cache Nix/devenv' });
35
35
  return steps.map((step, index) => ({ ...step, number: index + 2 }));
@@ -51,7 +51,7 @@ permissions:
51
51
  # actions:write lets the drift step dispatch the managed-files workflow.
52
52
  actions: write
53
53
  contents: read
54
- statuses: write
54
+ ${options.deploy ? ' deployments: write\n' : ''} statuses: write
55
55
 
56
56
  defaults:
57
57
  run:
@@ -167,11 +167,15 @@ function yamlLinesForStep(step, options) {
167
167
  case CiWorkflowStepKind.Deploy:
168
168
  return [
169
169
  ` - name: ${step.name}`,
170
- ' if:',
171
- " ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}',",
172
- ' github.event.repository.default_branch) }}',
170
+ ' if: >-',
171
+ ' ${{',
172
+ " (github.event_name == 'pull_request' &&",
173
+ ' contains(fromJSON(\'["opened","reopened","synchronize"]\'), github.event.action) &&',
174
+ ' github.event.pull_request.head.repo.full_name == github.repository) ||',
175
+ " (github.event_name == 'push' && github.ref == 'refs/heads/private')",
176
+ ' }}',
173
177
  ...deployEnvLines(options),
174
- ` run: smoo github-ci nx-deploy --configuration staging --mode affected --name "Deploy Staging" --step ${step.number}`,
178
+ ` run: smoo github-ci nx-deploy --mode run-many --name "Deploy Environment" --step ${step.number}`,
175
179
  ];
176
180
  case CiWorkflowStepKind.SaveNxCache:
177
181
  return [
@@ -220,6 +224,12 @@ function deployEnvLines(options) {
220
224
  ' env:',
221
225
  ' CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}',
222
226
  ' CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}',
227
+ ' GITHUB_CLIENT_SECRET: ${{ secrets.GITHUB_CLIENT_SECRET }}',
228
+ ' GITHUB_APP_PRIVATE_KEY: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}',
229
+ ' GITHUB_APP_PRIVATE_KEY_PEM: ${{ secrets.GITHUB_APP_PRIVATE_KEY_PEM }}',
230
+ ' OAUTH_STATE_SIGNING_KEY: ${{ secrets.OAUTH_STATE_SIGNING_KEY }}',
231
+ ' MAIL_CAPTURE_CONTROL_TOKEN: ${{ secrets.MAIL_CAPTURE_CONTROL_TOKEN }}',
232
+ ' TOKEN_ENCRYPTION_KEY: ${{ secrets.TOKEN_ENCRYPTION_KEY }}',
223
233
  ];
224
234
  }
225
235
  function nxSmartStep(step, target, name) {
@@ -1 +1 @@
1
- {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,UAAU,EAA2B,MAAM,gBAAgB,CAAC;AAM1F;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,yEAAyE,CAAC;AAU3G,sFAAsF;AACtF,iBAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAIlF;AAED,uCAAuC;AACvC,eAAO,MAAM,wBAAwB,0BAAoB,CAAC;AAE1D;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;8BAE8B;AAC9B,iBAAS,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAkCxG;AAED,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,iCAA2B,CAAC;AAExE;+EAC+E;AAC/E,iBAAS,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAqBtF;AAED,yCAAyC;AACzC,eAAO,MAAM,gCAAgC,kCAA4B,CAAC;AAE1E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAeD,UAAU,gBAAgB;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AA4GD,eAAO,MAAM,yBAAyB;;;GAAyE,CAAC;AAIhH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAG9G;AAmGD,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAMlF;AAED,0EAA0E;AAC1E,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,gBAAgB,CAY1G;AAsDD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxE;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBxE"}
1
+ {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,UAAU,EAA2B,MAAM,gBAAgB,CAAC;AAO1F;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,yEAAyE,CAAC;AAW3G,sFAAsF;AACtF,iBAAS,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAIlF;AAED,uCAAuC;AACvC,eAAO,MAAM,wBAAwB,0BAAoB,CAAC;AAE1D;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;8BAE8B;AAC9B,iBAAS,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAkCxG;AAED,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,iCAA2B,CAAC;AAExE;+EAC+E;AAC/E,iBAAS,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAqBtF;AAED,yCAAyC;AACzC,eAAO,MAAM,gCAAgC,kCAA4B,CAAC;AAE1E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAeD,UAAU,gBAAgB;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAkHD,eAAO,MAAM,yBAAyB;;;GAAyE,CAAC;AAIhH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAG9G;AAyGD,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAMlF;AAED,0EAA0E;AAC1E,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,gBAAgB,CAY1G;AA2DD,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxE;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBxE"}
@@ -5,6 +5,7 @@ import { PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-
5
5
  import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
6
6
  import { loadNxProjects, targetNamesFromProjects } from '../nx/index.js';
7
7
  import { renderCiWorkflowYaml } from './ci-workflow.js';
8
+ import { renderPrPreviewCleanupWorkflowYaml } from './pr-preview-cleanup-workflow.js';
8
9
  import { renderPublishWorkflowYaml } from './publish-workflow.js';
9
10
  /**
10
11
  * Repos may append their own content to a managed file below this marker —
@@ -156,6 +157,12 @@ const managedFiles = [
156
157
  source: 'ci-workflow',
157
158
  target: '.github/workflows/ci.yml',
158
159
  },
160
+ {
161
+ kind: 'generated',
162
+ source: 'pr-preview-cleanup-workflow',
163
+ target: '.github/workflows/pr-preview-cleanup.yml',
164
+ cloudflareDeployOnly: true,
165
+ },
159
166
  {
160
167
  kind: 'generated',
161
168
  source: 'publish-workflow',
@@ -208,6 +215,9 @@ function applyManagedFile(root, file, mode, context) {
208
215
  if (file.releasePackagesOnly === true && !context.hasReleasePackages && !context.hasProductionDeployTargets) {
209
216
  return { target: file.target, action: 'skipped' };
210
217
  }
218
+ if (file.cloudflareDeployOnly === true && context.stagingDeployProvider !== 'cloudflare') {
219
+ return { target: file.target, action: 'skipped' };
220
+ }
211
221
  const target = resolve(root, file.target);
212
222
  const content = getManagedContent(file, context);
213
223
  if (existsSync(target)) {
@@ -257,6 +267,9 @@ function getManagedContent(file, context) {
257
267
  runsOn: context.ciRunsOn,
258
268
  });
259
269
  }
270
+ if (file.source === 'pr-preview-cleanup-workflow') {
271
+ return renderPrPreviewCleanupWorkflowYaml({ runsOn: context.ciRunsOn });
272
+ }
260
273
  throw new Error(`Unknown generated managed file source ${file.source}`);
261
274
  }
262
275
  const sourceRoot = file.kind === 'raw' ? 'managed/raw' : 'managed/templates';
@@ -319,11 +332,16 @@ function deployTargetInfoFromTargets(targets, configuration) {
319
332
  if (!deploy) {
320
333
  return { exists: false };
321
334
  }
335
+ const baseCommandValue = deploy.options?.command ?? deploy.command;
336
+ const baseCommand = typeof baseCommandValue === 'string' ? baseCommandValue : '';
337
+ if (baseCommand.includes('smoo wrangler deploy-environment')) {
338
+ return { exists: true, provider: 'cloudflare' };
339
+ }
322
340
  const config = deploy.configurations?.[configuration];
323
341
  if (!config) {
324
342
  return { exists: false };
325
343
  }
326
- const commandValue = config.command ?? config.options?.command ?? deploy.options?.command ?? deploy.command;
344
+ const commandValue = config.command ?? config.options?.command ?? baseCommand;
327
345
  const command = typeof commandValue === 'string' ? commandValue : '';
328
346
  return { exists: true, provider: command.includes('wrangler ') ? 'cloudflare' : undefined };
329
347
  }
@@ -0,0 +1,5 @@
1
+ export interface PrPreviewCleanupWorkflowOptions {
2
+ runsOn?: string | string[];
3
+ }
4
+ export declare function renderPrPreviewCleanupWorkflowYaml(options?: PrPreviewCleanupWorkflowOptions): string;
5
+ //# sourceMappingURL=pr-preview-cleanup-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-preview-cleanup-workflow.d.ts","sourceRoot":"","sources":["../../src/monorepo/pr-preview-cleanup-workflow.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,+BAA+B;IAC9C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,kCAAkC,CAAC,OAAO,GAAE,+BAAoC,GAAG,MAAM,CAmCxG"}
@@ -0,0 +1,38 @@
1
+ /* biome-ignore-all lint/suspicious/noTemplateCurlyInString: GitHub Actions expressions are emitted literally. */
2
+ import { renderRunsOnLine } from './github-runs-on.js';
3
+ export function renderPrPreviewCleanupWorkflowYaml(options = {}) {
4
+ return `name: PR Preview Cleanup
5
+
6
+ on:
7
+ pull_request:
8
+ types: [closed]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ cleanup:
15
+ name: Cleanup PR environment
16
+ if: github.event.pull_request.head.repo.full_name == github.repository
17
+ ${renderRunsOnLine(options.runsOn)}
18
+ timeout-minutes: 15
19
+ defaults:
20
+ run:
21
+ working-directory: tooling/direnv
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v6.0.2
25
+ with:
26
+ filter: blob:none
27
+ fetch-depth: 1
28
+
29
+ - name: Setup Nix/devenv
30
+ uses: ./.github/actions/setup-devenv
31
+
32
+ - name: Cleanup PR environment
33
+ env:
34
+ CLOUDFLARE_API_TOKEN: \${{ secrets.CLOUDFLARE_API_TOKEN }}
35
+ CLOUDFLARE_ACCOUNT_ID: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
36
+ run: smoo wrangler cleanup-pr --pr \${{ github.event.pull_request.number }}
37
+ `;
38
+ }