cistack 5.2.0 → 5.4.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/README.md CHANGED
@@ -89,6 +89,8 @@ By default, `cistack` now generates a single GitHub Actions workflow that combin
89
89
 
90
90
  Dependabot remains a separate file in `.github/dependabot.yml`, because it is not a GitHub Actions workflow.
91
91
 
92
+ If you want preview deployments on Dependabot pull requests, add deployment credentials as Dependabot secrets too, not only Actions secrets. For Vercel, that means `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID`.
93
+
92
94
  ### Split mode
93
95
 
94
96
  If you prefer the old multi-file layout, set:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cistack",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "description": "Automatically generate GitHub Actions CI/CD pipelines by analysing your codebase",
5
5
  "main": "src/index.js",
6
6
  "types": "index.d.ts",
@@ -422,7 +422,7 @@ class WorkflowGenerator {
422
422
  if (previewSteps.length > 0) {
423
423
  jobs.preview = {
424
424
  name: `✨ Deploy → ${h.name} (Preview)`,
425
- if: "github.event_name == 'pull_request'",
425
+ if: "github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository",
426
426
  'runs-on': 'ubuntu-latest',
427
427
  environment: 'preview',
428
428
  steps: [
@@ -430,7 +430,7 @@ class WorkflowGenerator {
430
430
  ...previewSteps,
431
431
  {
432
432
  name: 'Comment PR',
433
- if: 'always()',
433
+ if: "always() && github.actor != 'dependabot[bot]'",
434
434
  uses: 'actions/github-script@v7',
435
435
  with: {
436
436
  script: `
@@ -883,20 +883,29 @@ class WorkflowGenerator {
883
883
  };
884
884
  steps.push(
885
885
  { name: 'Install Vercel CLI', run: 'npm install -g vercel' },
886
+ {
887
+ name: 'Validate Vercel credentials',
888
+ run: [
889
+ 'test -n "$VERCEL_TOKEN" || (echo "Missing VERCEL_TOKEN secret. Add it in GitHub Actions secrets." && exit 1)',
890
+ 'test -n "$VERCEL_ORG_ID" || (echo "Missing VERCEL_ORG_ID secret. Add it in GitHub Actions secrets." && exit 1)',
891
+ 'test -n "$VERCEL_PROJECT_ID" || (echo "Missing VERCEL_PROJECT_ID secret. Add it in GitHub Actions secrets." && exit 1)',
892
+ ].join('\n'),
893
+ env: vercelEnv,
894
+ },
886
895
  {
887
896
  name: 'Pull Vercel environment',
888
- run: `vercel pull --yes --environment=${isPreview ? 'preview' : 'production'} --token=\${{ secrets.VERCEL_TOKEN }}`,
897
+ run: `vercel pull --yes --environment=${isPreview ? 'preview' : 'production'} --token="$VERCEL_TOKEN"`,
889
898
  env: vercelEnv,
890
899
  },
891
900
  {
892
901
  name: 'Build project',
893
- run: `vercel build${prodFlag ? ' ' + prodFlag : ''} --token=\${{ secrets.VERCEL_TOKEN }}`,
902
+ run: `vercel build${prodFlag ? ' ' + prodFlag : ''} --token="$VERCEL_TOKEN"`,
894
903
  env: vercelEnv,
895
904
  },
896
905
  {
897
906
  name: 'Deploy to Vercel',
898
907
  id: 'deploy',
899
- run: `vercel deploy --prebuilt${prodFlag ? ' ' + prodFlag : ''} --token=\${{ secrets.VERCEL_TOKEN }} > deployment_url.txt && echo "DEPLOYMENT_URL=$(cat deployment_url.txt)" >> $GITHUB_ENV`,
908
+ run: `vercel deploy --prebuilt${prodFlag ? ' ' + prodFlag : ''} --token="$VERCEL_TOKEN" > deployment_url.txt && echo "DEPLOYMENT_URL=$(cat deployment_url.txt)" >> $GITHUB_ENV`,
900
909
  env: vercelEnv,
901
910
  },
902
911
  );
package/tests/run.js CHANGED
@@ -414,6 +414,38 @@ test('combineWorkflows preserves workflow-specific trigger scoping in the unifie
414
414
  assert(!combined.jobs.deploy_deploy.if.includes("github.ref_name == 'develop'"));
415
415
  });
416
416
 
417
+ test('Vercel deploy workflows validate secrets before running vercel pull', () => {
418
+ const projectDir = makeTempDir();
419
+ writeFiles(projectDir, {
420
+ 'package.json': json({
421
+ name: 'vercel-secrets-app',
422
+ version: '1.0.0',
423
+ scripts: {
424
+ build: 'next build',
425
+ },
426
+ }),
427
+ });
428
+
429
+ const workflows = new WorkflowGenerator(
430
+ makeJsProject({
431
+ hosting: [{ name: 'Vercel', secrets: ['VERCEL_TOKEN', 'VERCEL_ORG_ID', 'VERCEL_PROJECT_ID'] }],
432
+ frameworks: [{ name: 'Next.js', confidence: 1, buildDir: '.next' }],
433
+ }),
434
+ projectDir
435
+ ).generate();
436
+
437
+ const deploy = parseWorkflow(workflows.find((workflow) => workflow.filename === 'deploy.yml').content);
438
+ const deploySteps = deploy.jobs.deploy.steps;
439
+ const validateStep = deploySteps.find((step) => step.name === 'Validate Vercel credentials');
440
+ const pullStep = deploySteps.find((step) => step.name === 'Pull Vercel environment');
441
+
442
+ assert(validateStep);
443
+ assert(validateStep.run.includes('Missing VERCEL_TOKEN secret'));
444
+ assert(validateStep.run.includes('Missing VERCEL_ORG_ID secret'));
445
+ assert(validateStep.run.includes('Missing VERCEL_PROJECT_ID secret'));
446
+ assert.equal(pullStep.run, 'vercel pull --yes --environment=production --token="$VERCEL_TOKEN"');
447
+ });
448
+
417
449
  test('Single-layout monorepos still generate the root workspace matrix CI', () => {
418
450
  const projectDir = makeTempDir();
419
451
  const packages = [
@@ -528,6 +560,37 @@ test('Netlify preview configuration uses the detected production branch instead
528
560
  assert.equal(previewStep.with['production-branch'], 'release');
529
561
  });
530
562
 
563
+ test('Preview deploy jobs run for same-repo pull requests, including Dependabot PRs', () => {
564
+ const projectDir = makeTempDir();
565
+ writeFiles(projectDir, {
566
+ 'package.json': json({
567
+ name: 'preview-guard-app',
568
+ version: '1.0.0',
569
+ scripts: {
570
+ build: 'next build',
571
+ },
572
+ }),
573
+ });
574
+
575
+ const generator = new WorkflowGenerator(
576
+ makeJsProject({
577
+ hosting: [{ name: 'Vercel', secrets: ['VERCEL_TOKEN', 'VERCEL_ORG_ID', 'VERCEL_PROJECT_ID'] }],
578
+ frameworks: [{ name: 'Next.js', confidence: 1, buildDir: '.next' }],
579
+ }),
580
+ projectDir
581
+ );
582
+
583
+ const deploy = generator.generate().find((workflow) => workflow.filename === 'deploy.yml');
584
+ const parsed = parseWorkflow(deploy.content);
585
+ const commentStep = parsed.jobs.preview.steps.find((step) => step.name === 'Comment PR');
586
+
587
+ assert.equal(
588
+ parsed.jobs.preview.if,
589
+ "github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository"
590
+ );
591
+ assert.equal(commentStep.if, "always() && github.actor != 'dependabot[bot]'");
592
+ });
593
+
531
594
  test('Generic JavaScript builds no longer upload a fake dist artifact when no build directory is known', () => {
532
595
  const projectDir = makeTempDir();
533
596
  writeFiles(projectDir, {