@catladder/cli 0.0.0-next-78d43d4a → 0.0.0-next-6916db3c

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 (65) hide show
  1. package/dist/apps/cli/src/catci.js +2 -2
  2. package/dist/apps/cli/src/catci.js.map +1 -1
  3. package/dist/apps/cli/src/commands/project/commandGitlabCi.js +2 -1
  4. package/dist/apps/cli/src/commands/project/commandGitlabCi.js.map +1 -1
  5. package/dist/apps/cli/src/commands/security/commandSecurityAuditCiJob.js +2 -2
  6. package/dist/apps/cli/src/commands/security/commandSecurityAuditCiJob.js.map +1 -1
  7. package/dist/apps/cli/src/commands/security/commandSecurityAuditCreate.js +2 -2
  8. package/dist/apps/cli/src/commands/security/commandSecurityAuditCreate.js.map +1 -1
  9. package/dist/apps/cli/src/publish/npmPublishJob.js +22 -5
  10. package/dist/apps/cli/src/publish/npmPublishJob.js.map +1 -1
  11. package/dist/apps/cli/src/utils/gitlabHost.d.ts +14 -0
  12. package/dist/apps/cli/src/utils/gitlabHost.js +27 -0
  13. package/dist/apps/cli/src/utils/gitlabHost.js.map +1 -0
  14. package/dist/bundles/catci/index.js +10 -4
  15. package/dist/bundles/catenv/index.js +137 -36
  16. package/dist/bundles/cli/index.js +177 -41
  17. package/dist/bundles/runner-images/kubernetes/helm-charts/the-panter-chart/templates/_helpers.tpl +0 -10
  18. package/dist/bundles/runner-images/kubernetes/helm-charts/the-panter-chart/templates/app.ingress.yaml +0 -5
  19. package/dist/bundles/runner-images/kubernetes/helm-charts/the-panter-chart/values.yaml +0 -1
  20. package/dist/bundles/runner-images/npm-publish/Dockerfile +12 -0
  21. package/dist/bundles/runner-images/semantic-release/scripts/semanticRelease +4 -3
  22. package/dist/bundles/skills/catladder-deploys/references/deploy-types.md +3 -2
  23. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +70 -24
  24. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +11 -1
  26. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  27. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js +5 -1
  28. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js.map +1 -1
  29. package/dist/packages/pipeline/src/backends/index.d.ts +6 -0
  30. package/dist/packages/pipeline/src/backends/index.js +9 -2
  31. package/dist/packages/pipeline/src/backends/index.js.map +1 -1
  32. package/dist/packages/pipeline/src/context/getEnvironmentVariables.d.ts +1 -1
  33. package/dist/packages/pipeline/src/customImages/shippedImages.js +1 -0
  34. package/dist/packages/pipeline/src/customImages/shippedImages.js.map +1 -1
  35. package/dist/packages/pipeline/src/deploy/base/deploy.js +1 -0
  36. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  37. package/dist/packages/pipeline/src/deploy/npmPackage/deployJob.js +6 -1
  38. package/dist/packages/pipeline/src/deploy/npmPackage/deployJob.js.map +1 -1
  39. package/dist/packages/pipeline/src/deploy/pages/deployJob.js +16 -5
  40. package/dist/packages/pipeline/src/deploy/pages/deployJob.js.map +1 -1
  41. package/dist/packages/pipeline/src/pipeline/generatePipelineFiles.js +10 -0
  42. package/dist/packages/pipeline/src/pipeline/generatePipelineFiles.js.map +1 -1
  43. package/dist/packages/pipeline/src/runner/index.d.ts +1 -1
  44. package/dist/packages/pipeline/src/runner/index.js.map +1 -1
  45. package/dist/packages/pipeline/src/types/config.d.ts +3 -3
  46. package/dist/packages/pipeline/src/types/jobDefinition.d.ts +1 -1
  47. package/dist/packages/pipeline/src/types/jobs.d.ts +9 -0
  48. package/dist/packages/pipeline/src/types/jobs.js.map +1 -1
  49. package/dist/packages/pipeline/src/utils/writeFiles.d.ts +6 -0
  50. package/dist/packages/pipeline/src/utils/writeFiles.js +8 -2
  51. package/dist/packages/pipeline/src/utils/writeFiles.js.map +1 -1
  52. package/dist/runner-images/kubernetes/helm-charts/the-panter-chart/templates/_helpers.tpl +0 -10
  53. package/dist/runner-images/kubernetes/helm-charts/the-panter-chart/templates/app.ingress.yaml +0 -5
  54. package/dist/runner-images/kubernetes/helm-charts/the-panter-chart/values.yaml +0 -1
  55. package/dist/runner-images/npm-publish/Dockerfile +12 -0
  56. package/dist/runner-images/semantic-release/scripts/semanticRelease +4 -3
  57. package/dist/skills/catladder-deploys/references/deploy-types.md +3 -2
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +1 -1
  60. package/src/catci.ts +2 -3
  61. package/src/commands/project/commandGitlabCi.ts +2 -1
  62. package/src/commands/security/commandSecurityAuditCiJob.ts +2 -3
  63. package/src/commands/security/commandSecurityAuditCreate.ts +2 -3
  64. package/src/publish/npmPublishJob.ts +32 -6
  65. package/src/utils/gitlabHost.ts +23 -0
@@ -2959,6 +2959,7 @@ const child_process_promise_1 = __nccwpck_require__(42242);
2959
2959
  const lodash_es_1 = __nccwpck_require__(50866);
2960
2960
  const open_1 = __importDefault(__nccwpck_require__(81776));
2961
2961
  const defineCommand_1 = __nccwpck_require__(40317);
2962
+ const gitlabHost_1 = __nccwpck_require__(83534);
2962
2963
  const gitlab_1 = __nccwpck_require__(8680);
2963
2964
  const statusEmojiMap = {
2964
2965
  failed: "🙀",
@@ -3033,7 +3034,7 @@ exports.commandCiJobLog = (0, defineCommand_1.defineCommand)({
3033
3034
  const id = selectedJob.id;
3034
3035
  let finished = false;
3035
3036
  while (!finished) {
3036
- const trace = await (0, child_process_promise_1.exec)(`curl -s --header "PRIVATE-TOKEN: ${await (0, gitlab_1.getGitlabToken)(ctx)}" "https://git.panter.ch/api/v4/projects/${projectId}/jobs/${id}/trace"`);
3037
+ const trace = await (0, child_process_promise_1.exec)(`curl -s --header "PRIVATE-TOKEN: ${await (0, gitlab_1.getGitlabToken)(ctx)}" "${await (0, gitlabHost_1.getGitlabHostUrl)()}/api/v4/projects/${projectId}/jobs/${id}/trace"`);
3037
3038
  const job = await (0, gitlab_1.doGitlabRequest)(ctx, `projects/${projectId}/jobs/${id}`);
3038
3039
  if (trace.stdout) {
3039
3040
  ctx.log(trace.stdout);
@@ -4725,7 +4726,7 @@ const defineCommand_1 = __nccwpck_require__(40317);
4725
4726
  const rest_1 = __nccwpck_require__(37650);
4726
4727
  const evaluateSecurityAudit_1 = __nccwpck_require__(79392);
4727
4728
  const createSecurityAuditMergeRequest_1 = __nccwpck_require__(91276);
4728
- const GITLAB_HOST = "https://git.panter.ch";
4729
+ const gitlabHost_1 = __nccwpck_require__(83534);
4729
4730
  exports.commandSecurityAuditCiJob = (0, defineCommand_1.defineCommand)({
4730
4731
  name: "security audit ci-job",
4731
4732
  description: "Evaluates security audit document. Creates MR with template if missing.",
@@ -4763,7 +4764,7 @@ exports.commandSecurityAuditCiJob = (0, defineCommand_1.defineCommand)({
4763
4764
  const mainBranch = await ctx.get("mainBranch");
4764
4765
  const projectId = await ctx.get("projectId");
4765
4766
  const userId = await ctx.get("userId");
4766
- const api = new rest_1.Gitlab({ host: GITLAB_HOST, token });
4767
+ const api = new rest_1.Gitlab({ host: await (0, gitlabHost_1.getGitlabHostUrl)(), token });
4767
4768
  const mr = await (0, createSecurityAuditMergeRequest_1.createSecurityAuditMergeRequest)({
4768
4769
  api,
4769
4770
  mainBranch,
@@ -4803,7 +4804,7 @@ exports.commandSecurityAuditCreate = void 0;
4803
4804
  const defineCommand_1 = __nccwpck_require__(40317);
4804
4805
  const rest_1 = __nccwpck_require__(37650);
4805
4806
  const createSecurityAuditMergeRequest_1 = __nccwpck_require__(91276);
4806
- const GITLAB_HOST = "https://git.panter.ch";
4807
+ const gitlabHost_1 = __nccwpck_require__(83534);
4807
4808
  exports.commandSecurityAuditCreate = (0, defineCommand_1.defineCommand)({
4808
4809
  name: "security audit create",
4809
4810
  description: "Creates a MR with the latest security audit template document",
@@ -4835,7 +4836,7 @@ exports.commandSecurityAuditCreate = (0, defineCommand_1.defineCommand)({
4835
4836
  const mainBranch = await ctx.get("mainBranch");
4836
4837
  const projectId = await ctx.get("projectId");
4837
4838
  const userId = await ctx.get("userId");
4838
- const api = new rest_1.Gitlab({ host: GITLAB_HOST, token });
4839
+ const api = new rest_1.Gitlab({ host: await (0, gitlabHost_1.getGitlabHostUrl)(), token });
4839
4840
  const result = await (0, createSecurityAuditMergeRequest_1.createSecurityAuditMergeRequest)({
4840
4841
  api,
4841
4842
  mainBranch,
@@ -6722,6 +6723,40 @@ exports.upsertAllVariables = upsertAllVariables;
6722
6723
 
6723
6724
  /***/ }),
6724
6725
 
6726
+ /***/ 83534:
6727
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6728
+
6729
+ "use strict";
6730
+
6731
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6732
+ exports.getGitlabHostUrl = void 0;
6733
+ const gitProjectInformation_1 = __nccwpck_require__(83851);
6734
+ /**
6735
+ * the base url of the gitlab instance this project lives on.
6736
+ *
6737
+ * Never hardcode an instance: catladder is used outside panter, and the
6738
+ * host was baked in as https://git.panter.ch in several places, which
6739
+ * silently broke every consumer on another gitlab.
6740
+ *
6741
+ * - in a gitlab CI job, `CI_SERVER_URL` is the authoritative value
6742
+ * (predefined, already includes the scheme)
6743
+ * - locally, derive it from the git remote
6744
+ *
6745
+ * @param remoteName the remote to derive from outside CI (default origin)
6746
+ */
6747
+ const getGitlabHostUrl = async (remoteName = "origin") => {
6748
+ const fromCi = process.env.CI_SERVER_URL;
6749
+ if (fromCi) {
6750
+ return fromCi.replace(/\/+$/, "");
6751
+ }
6752
+ const { gitRemoteHost } = await (0, gitProjectInformation_1.getGitRemoteHostAndPath)(remoteName);
6753
+ return `https://${gitRemoteHost}`;
6754
+ };
6755
+ exports.getGitlabHostUrl = getGitlabHostUrl;
6756
+ //# sourceMappingURL=gitlabHost.js.map
6757
+
6758
+ /***/ }),
6759
+
6725
6760
  /***/ 66697:
6726
6761
  /***/ ((__unused_webpack_module, exports) => {
6727
6762
 
@@ -8924,6 +8959,11 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
8924
8959
  ? runScript
8925
8960
  : scripts.add(id, runScript);
8926
8961
  const artifactPaths = (_g = (_f = job.artifacts) === null || _f === void 0 ? void 0 : _f.paths) !== null && _g !== void 0 ? _g : [];
8962
+ // github pages: the built site is handed to the official actions
8963
+ // instead of being uploaded as a plain artifact. `deploy-pages`
8964
+ // replaces the whole site, so there is no path_prefix equivalent —
8965
+ // review previews are rejected upstream (see the pages deploy type).
8966
+ const pagesPublishDir = job.pages ? artifactPaths[0] : undefined;
8927
8967
  const declaredServices = ((_h = job.services) !== null && _h !== void 0 ? _h : []).map((service) => typeof service === "string" ? { name: service } : service);
8928
8968
  // docker runs natively on github runners, so gitlab's docker-in-docker
8929
8969
  // services are dropped. The job container stays: the runner mounts the
@@ -8969,14 +9009,38 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
8969
9009
  */
8970
9010
  const referencesSecrets = Object.values(env).some((value) => typeof value === "string" &&
8971
9011
  (value.includes("${{ secrets.") || value.includes("${{ vars.")));
8972
- const githubEnvironment = job.environment && isComponent
9012
+ const githubEnvironment = pagesPublishDir
9013
+ ? // github requires pages deployments to target the `github-pages`
9014
+ // environment, and only the deploy step knows the final url.
9015
+ // `secrets-sync-github` derives its targets from the generated
9016
+ // workflows, so any secret this job references is synced there.
9017
+ {
9018
+ name: "github-pages",
9019
+ url: "${{ steps.deployment.outputs.page_url }}",
9020
+ }
9021
+ : job.environment && isComponent
9022
+ ? {
9023
+ name: `${context.env}-${context.name}`,
9024
+ ...(createsJobEnv ? { url: "${{ steps.main.outputs.url }}" } : {}),
9025
+ }
9026
+ : referencesSecrets
9027
+ ? { name: context.env }
9028
+ : undefined;
9029
+ // declaring `permissions` drops ALL default token grants, so a job
9030
+ // that needs any special grant must also re-declare the ordinary ones:
9031
+ // `contents: read` for the checkout and `packages: read` to pull its
9032
+ // job image from the repo registry.
9033
+ const specialPermissions = {
9034
+ ...(pagesPublishDir ? { pages: "write", "id-token": "write" } : {}),
9035
+ ...(job.idToken ? { "id-token": "write" } : {}),
9036
+ };
9037
+ const permissions = Object.keys(specialPermissions).length
8973
9038
  ? {
8974
- name: `${context.env}-${context.name}`,
8975
- ...(createsJobEnv ? { url: "${{ steps.main.outputs.url }}" } : {}),
9039
+ contents: "read",
9040
+ ...(resolved.fromRepoRegistry ? { packages: "read" } : {}),
9041
+ ...specialPermissions,
8976
9042
  }
8977
- : referencesSecrets
8978
- ? { name: context.env }
8979
- : undefined;
9043
+ : undefined;
8980
9044
  const githubJob = {
8981
9045
  name: githubJobName(context, job.name),
8982
9046
  "runs-on": "ubuntu-latest",
@@ -8999,6 +9063,7 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
8999
9063
  : {}),
9000
9064
  ...(Object.keys(services).length > 0 ? { services } : {}),
9001
9065
  ...(githubEnvironment ? { environment: githubEnvironment } : {}),
9066
+ ...(permissions ? { permissions } : {}),
9002
9067
  env,
9003
9068
  steps: [
9004
9069
  ...(skipCheckout
@@ -9018,25 +9083,41 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
9018
9083
  run,
9019
9084
  shell: "bash",
9020
9085
  },
9021
- ...(uploadProviderIds.has(id) || artifactPaths.length > 0
9022
- ? artifactPaths.length > 0
9023
- ? [
9024
- {
9025
- name: "Upload artifacts",
9026
- uses: "actions/upload-artifact@v4",
9027
- with: {
9028
- name: id,
9029
- path: artifactPaths.join("\n"),
9030
- "if-no-files-found": "warn",
9031
- // gitlab artifacts include everything; upload-artifact
9032
- // silently drops hidden files by default — which is
9033
- // exactly where next.js builds live (.next)
9034
- "include-hidden-files": true,
9035
- },
9036
- },
9037
- ]
9038
- : []
9086
+ ...(pagesPublishDir
9087
+ ? [
9088
+ {
9089
+ name: "Upload pages artifact",
9090
+ uses: "actions/upload-pages-artifact@v3",
9091
+ with: { path: pagesPublishDir },
9092
+ },
9093
+ {
9094
+ name: "Deploy to github pages",
9095
+ id: "deployment",
9096
+ uses: "actions/deploy-pages@v4",
9097
+ },
9098
+ ]
9039
9099
  : []),
9100
+ ...(pagesPublishDir
9101
+ ? []
9102
+ : uploadProviderIds.has(id) || artifactPaths.length > 0
9103
+ ? artifactPaths.length > 0
9104
+ ? [
9105
+ {
9106
+ name: "Upload artifacts",
9107
+ uses: "actions/upload-artifact@v4",
9108
+ with: {
9109
+ name: id,
9110
+ path: artifactPaths.join("\n"),
9111
+ "if-no-files-found": "warn",
9112
+ // gitlab artifacts include everything; upload-artifact
9113
+ // silently drops hidden files by default — which is
9114
+ // exactly where next.js builds live (.next)
9115
+ "include-hidden-files": true,
9116
+ },
9117
+ },
9118
+ ]
9119
+ : []
9120
+ : []),
9040
9121
  ],
9041
9122
  };
9042
9123
  return [id, githubJob];
@@ -9526,8 +9607,10 @@ const createGitlabPipeline_1 = __nccwpck_require__(94411);
9526
9607
  const gitlabReleaseJobs_1 = __nccwpck_require__(75512);
9527
9608
  const getPipelineStages_1 = __nccwpck_require__(29641);
9528
9609
  const sortGitLabJobDefProps_1 = __nccwpck_require__(71625);
9610
+ const writeFiles_1 = __nccwpck_require__(7396);
9529
9611
  const CATLADDER_GENERATED_FOLDER = ".catladder-generated";
9530
9612
  const GITLAB_GENERATED_FOLDER = CATLADDER_GENERATED_FOLDER + "/gitlab";
9613
+ const GITLAB_MAIN_FILE = ".gitlab-ci.yml";
9531
9614
  class GitlabBackend {
9532
9615
  constructor() {
9533
9616
  this.type = "gitlab";
@@ -9535,12 +9618,20 @@ class GitlabBackend {
9535
9618
  }
9536
9619
  async cleanup() {
9537
9620
  await (0, promises_1.rm)(this.generatedFolder, { force: true, recursive: true });
9621
+ // `.gitlab-ci.yml` lives outside the generated folder, so it used to
9622
+ // survive disabling the gitlab backend and kept a stale pipeline
9623
+ // running. Only remove it when it is ours: a project that generates
9624
+ // github only may well hand-maintain a gitlab file of its own.
9625
+ const mainFile = await (0, promises_1.readFile)(GITLAB_MAIN_FILE, "utf-8").catch(() => null);
9626
+ if (mainFile === null || mainFile === void 0 ? void 0 : mainFile.includes(writeFiles_1.GENERATED_FILE_MARKER)) {
9627
+ await (0, promises_1.rm)(GITLAB_MAIN_FILE, { force: true });
9628
+ }
9538
9629
  }
9539
9630
  async createFiles(config) {
9540
9631
  const images = this.createImagesPlan(config);
9541
9632
  const includes = await this.createIncludes(config, images);
9542
9633
  const mainFile = {
9543
- path: ".gitlab-ci.yml",
9634
+ path: GITLAB_MAIN_FILE,
9544
9635
  content: {
9545
9636
  include: includes.map((i) => i.path),
9546
9637
  },
@@ -9849,7 +9940,11 @@ const getFullReferencedJobNameFromWorkspace = (referencedJobName, workspaceName,
9849
9940
  const getJobName = (need) => (0, lodash_es_1.isObject)(need) ? need.job : need;
9850
9941
  const makeGitlabJob = (context, job, allJobs, images, baseRules) => {
9851
9942
  var _a, _b, _c;
9852
- const { environment, envMode, requires, provides, name, needs, jobTags, script, variables, runnerVariables, gate, when: jobWhen, allow_failure: jobAllowFailure, cache: jobCache, caches, image: jobImage, ...rest } = job;
9943
+ const { environment, envMode, requires, provides, name, needs, jobTags, script, variables, runnerVariables, gate, when: jobWhen, allow_failure: jobAllowFailure, cache: jobCache, caches, image: jobImage,
9944
+ // github lowers this to `permissions: id-token: write`; gitlab has
9945
+ // no counterpart, and anything left in `rest` lands in the job yaml
9946
+ // verbatim ("config contains unknown keys: idtoken")
9947
+ idToken: _idToken, ...rest } = job;
9853
9948
  // catladder image references resolve to an image built in the repo's
9854
9949
  // own registry; the job additionally depends on the image build job
9855
9950
  const resolvedImage = images.resolve(jobImage);
@@ -10518,7 +10613,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10518
10613
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
10519
10614
  };
10520
10615
  Object.defineProperty(exports, "__esModule", ({ value: true }));
10521
- exports.getEnabledPipelineTypes = exports.getVaultConfig = exports.getPipelineGitRemote = exports.getPipelineOptions = exports.getPipelineBackend = void 0;
10616
+ exports.getEnabledPipelineTypes = exports.getVaultConfig = exports.getPipelineGitRemote = exports.getPipelineOptions = exports.getPipelineBackend = exports.getAllPipelineTypes = void 0;
10522
10617
  const github_1 = __nccwpck_require__(48);
10523
10618
  const gitlab_1 = __nccwpck_require__(10744);
10524
10619
  __exportStar(__nccwpck_require__(33415), exports);
@@ -10528,6 +10623,13 @@ const BACKENDS = {
10528
10623
  gitlab: () => new gitlab_1.GitlabBackend(),
10529
10624
  github: () => new github_1.GithubBackend(),
10530
10625
  };
10626
+ /**
10627
+ * every pipeline type catladder knows how to generate — regardless of
10628
+ * what the config enables. Cleanup uses this to also reach backends that
10629
+ * were switched off (their files would otherwise linger).
10630
+ */
10631
+ const getAllPipelineTypes = () => Object.keys(BACKENDS);
10632
+ exports.getAllPipelineTypes = getAllPipelineTypes;
10531
10633
  const getPipelineBackend = (type) => {
10532
10634
  const createBackend = BACKENDS[type];
10533
10635
  if (!createBackend) {
@@ -10570,7 +10672,7 @@ exports.getVaultConfig = getVaultConfig;
10570
10672
  const getEnabledPipelineTypes = (config) => {
10571
10673
  var _a;
10572
10674
  if (config.pipelines) {
10573
- const enabled = Object.keys(BACKENDS).filter((type) => { var _a; return (_a = config.pipelines) === null || _a === void 0 ? void 0 : _a[type]; });
10675
+ const enabled = (0, exports.getAllPipelineTypes)().filter((type) => { var _a; return (_a = config.pipelines) === null || _a === void 0 ? void 0 : _a[type]; });
10574
10676
  if (enabled.length === 0) {
10575
10677
  throw new Error("`pipelines` is configured, but no pipeline type is enabled");
10576
10678
  }
@@ -13767,6 +13869,7 @@ exports.RUNNER_IMAGE_BUILD_CONTEXT = {
13767
13869
  "docker-build": "self",
13768
13870
  "semantic-release": "self",
13769
13871
  changesets: "self",
13872
+ "npm-publish": "self",
13770
13873
  "agent-claude": "self",
13771
13874
  };
13772
13875
  /**
@@ -13906,6 +14009,7 @@ class DeployJob extends jobs_1.CatladderJob {
13906
14009
  artifacts: jobDefinition.artifacts,
13907
14010
  services: jobDefinition.services,
13908
14011
  pages: jobDefinition.pages,
14012
+ idToken: jobDefinition.idToken,
13909
14013
  allow_failure: (_e = jobDefinition.allow_failure) !== null && _e !== void 0 ? _e : deployConfig === null || deployConfig === void 0 ? void 0 : deployConfig.allowFailure,
13910
14014
  envMode: "stagePerEnv", // makes it easier to run manual tasks er env
13911
14015
  needs: [],
@@ -16163,7 +16267,12 @@ const createNpmPackageDeployJobs = (context) => {
16163
16267
  ];
16164
16268
  return (0, base_1.createDeployementJobs)(context, {
16165
16269
  deploy: {
16166
- image: (0, runner_1.getRunnerImage)("jobs-default"),
16270
+ // dedicated image: publishing needs npm >= 11.5.1 for trusted
16271
+ // publishing (OIDC), which jobs-default's node does not carry
16272
+ image: (0, runner_1.getRunnerImage)("npm-publish"),
16273
+ // github: lowered to `id-token: write` so npm can exchange the
16274
+ // workflow's OIDC token for short-lived publish credentials
16275
+ idToken: true,
16167
16276
  script: [
16168
16277
  // catci derives version + dist-tag from the pipeline trigger,
16169
16278
  // sets the version in package.json and runs npm publish
@@ -16220,9 +16329,14 @@ const createPagesDeployJobs = async (context) => {
16220
16329
  // should not happen
16221
16330
  throw new Error("deploy config is not pages");
16222
16331
  }
16223
- if (context.pipelineType === "github") {
16224
- // don't fail the whole github generation of a multi-backend project
16225
- console.warn(`component "${context.name}": the pages deploy type is not yet supported on the github backend — no deploy jobs generated`);
16332
+ const onGithub = context.pipelineType === "github";
16333
+ if (onGithub && context.environment.envType === "review") {
16334
+ // github pages serves ONE site per repository and deploy-pages
16335
+ // replaces all of it — there is no path_prefix equivalent, so per-PR
16336
+ // previews cannot exist (deploy-pages has a `preview` input, but it
16337
+ // is alpha and not publicly available). Warn instead of throwing, so
16338
+ // the github generation of a multi-backend project still succeeds.
16339
+ console.warn(`component "${context.name}": github pages cannot host per-merge-request previews (one site per repository) — no review deploy job generated`);
16226
16340
  return [];
16227
16341
  }
16228
16342
  // review environments publish under their own path prefix (gitlab
@@ -16245,8 +16359,14 @@ const createPagesDeployJobs = async (context) => {
16245
16359
  : []),
16246
16360
  ...deployConfig.script,
16247
16361
  // the gitlab environment url should point at the published site
16248
- // (the base deploy job reports $ROOT_URL as environment url)
16249
- `export ROOT_URL="$CI_PAGES_URL${pathPrefix ? `/${pathPrefix}` : ""}"`,
16362
+ // (the base deploy job reports $ROOT_URL as environment url).
16363
+ // On github the url is only known after deploy-pages ran, so the
16364
+ // backend takes it from that step's output instead.
16365
+ ...(onGithub
16366
+ ? []
16367
+ : [
16368
+ `export ROOT_URL="$CI_PAGES_URL${pathPrefix ? `/${pathPrefix}` : ""}"`,
16369
+ ]),
16250
16370
  ],
16251
16371
  variables: {},
16252
16372
  runnerVariables: {
@@ -17235,6 +17355,16 @@ async function generatePipelineFiles(context, pipelineType) {
17235
17355
  await (0, promises_1.rm)(jobImagesPlan_1.GENERATED_IMAGES_FOLDER, { recursive: true, force: true });
17236
17356
  // same for the materialized catci bundle (union across backends)
17237
17357
  await (0, promises_1.rm)(shippedCatci_1.GENERATED_CATCI_FOLDER, { recursive: true, force: true });
17358
+ // clean up the backends that are NOT enabled (any more): each
17359
+ // backend only ever removed its own files while it was enabled, so
17360
+ // disabling one in `pipelines` used to leave its pipeline files
17361
+ // behind forever — and a stale `.gitlab-ci.yml` keeps running a
17362
+ // stale pipeline. Only safe on a full generation, where the enabled
17363
+ // set is authoritative.
17364
+ const disabledTypes = (0, backends_1.getAllPipelineTypes)().filter((type) => !pipelineTypes.includes(type));
17365
+ for (const type of disabledTypes) {
17366
+ await (0, backends_1.getPipelineBackend)(type).cleanup();
17367
+ }
17238
17368
  // agent skills are backend-independent, materialized once per full
17239
17369
  // generation
17240
17370
  await (0, agentSkills_1.generateAgentSkills)(context);
@@ -18199,9 +18329,15 @@ exports.removeUndefined = removeUndefined;
18199
18329
  "use strict";
18200
18330
 
18201
18331
  Object.defineProperty(exports, "__esModule", ({ value: true }));
18202
- exports.FileWriter = exports.yamlStringifyOptions = void 0;
18332
+ exports.FileWriter = exports.yamlStringifyOptions = exports.GENERATED_FILE_MARKER = void 0;
18203
18333
  const promises_1 = __nccwpck_require__(91943);
18204
18334
  const yaml_1 = __nccwpck_require__(33483);
18335
+ /**
18336
+ * the line that marks a file as catladder-generated. Cleanup code uses
18337
+ * it to tell a generated file apart from a hand-maintained one at the
18338
+ * same path (e.g. a `.gitlab-ci.yml` a project wrote itself).
18339
+ */
18340
+ exports.GENERATED_FILE_MARKER = "🐱 🔨 This file is generated by catladder";
18205
18341
  exports.yamlStringifyOptions = {
18206
18342
  // prevents colapsing long command statements into multiple lines
18207
18343
  lineWidth: 0,
@@ -18261,7 +18397,7 @@ class FileWriter {
18261
18397
  getAutoGeneratedHeader(commentChar) {
18262
18398
  return [
18263
18399
  "-------------------------------------------------",
18264
- `🐱 🔨 This file is generated by catladder`,
18400
+ exports.GENERATED_FILE_MARKER,
18265
18401
  `🚨 Do not edit this file manually 🚨`,
18266
18402
  "-------------------------------------------------",
18267
18403
  ]
@@ -190,13 +190,3 @@ env:
190
190
 
191
191
 
192
192
 
193
- {{- define "visualReviewSnippet" -}}
194
- <script
195
- data-project-id='{{.Values.gitlab.projectId}}'
196
- data-merge-request-id='{{.Values.gitlab.mergeRequestId}}'
197
- data-mr-url='https://git.panter.ch'
198
- data-project-path='{{.Values.gitlab.projectPath}}'
199
- id='review-app-toolbar-script'
200
- src='https://git.panter.ch/assets/webpack/visual_review_toolbar.js'>
201
- </script>
202
- {{- end -}}
@@ -16,11 +16,6 @@ metadata:
16
16
  nginx.ingress.kubernetes.io/rewrite-target: /$1
17
17
  nginx.ingress.kubernetes.io/configuration-snippet: |
18
18
  more_set_headers "x-build-id: {{ .Values.global.BUILD_ID }}";
19
- {{- if and .Values.gitlab.visualReviewEnabled (and .Values.global.ENV_SHORT (eq .Values.global.ENV_SHORT "review"))}}
20
- proxy_set_header Accept-Encoding "";
21
- sub_filter_once on;
22
- sub_filter "</head>" "{{- include "visualReviewSnippet" . | nindent 8 -}}</head>";
23
- {{- end }}
24
19
  {{- range $key, $value := .Values.ingress.annotations }}
25
20
  {{ $key }}: {{ $value | quote }}
26
21
  {{- end }}
@@ -112,7 +112,6 @@ application:
112
112
  successThreshold: 1
113
113
 
114
114
  gitlab:
115
- visualReviewEnabled: false
116
115
  app:
117
116
  env:
118
117
  projectPath:
@@ -0,0 +1,12 @@
1
+ FROM node:lts-alpine
2
+
3
+ RUN apk add --no-cache bash git
4
+
5
+ # npm trusted publishing (OIDC) needs npm >= 11.5.1; the npm bundled
6
+ # with the node image is not guaranteed to be new enough, so pin the
7
+ # major explicitly. Without it npm silently falls back to token auth.
8
+ RUN npm install -g npm@11
9
+
10
+ # no publish logic baked in: it lives in catci, which pipeline
11
+ # generation materializes into the repository checkout
12
+ # (.catladder-generated/catci)
@@ -45,10 +45,11 @@ if [ "$GITHUB_ACTIONS" = "true" ]; then
45
45
  # is gitlab-specific)
46
46
  PUBLISH_PLUGIN=""
47
47
  else
48
+ # no gitlabUrl: the plugin derives it from the CI-provided variables
49
+ # of the running instance. It used to be hardcoded to panter's gitlab,
50
+ # which broke the release on every other instance.
48
51
  PUBLISH_PLUGIN=',
49
- ["@semantic-release/gitlab", {
50
- "gitlabUrl": "https://git.panter.ch"
51
- }]'
52
+ "@semantic-release/gitlab"'
52
53
  fi
53
54
 
54
55
  if [ ! -e .releaserc ]; then
@@ -54,11 +54,12 @@ resources with `yarn catladder project setup`; detect drift with
54
54
  - `registry?: string` — registry url, default `https://registry.npmjs.org/`.
55
55
  - `distTag?: string` — overrides the derived dist-tag.
56
56
  - Version/dist-tag derivation: tagged release (`prod`) → tag version under `latest`; branch/MR → `0.0.0-<branch-slug>-<sha>` under `canary` (branches `next`/`beta` publish under their own dist-tag).
57
- - Requires the `NPM_TOKEN` secret (managed like any catladder secret). Disable staging on the component (`env: { stage: false }`) so tagged releases publish directly.
57
+ - Auth: the `NPM_TOKEN` secret (managed like any catladder secret), or — on github — **trusted publishing** with no stored token: register the package's trusted publisher on npmjs.com with the repo and the publishing **workflow filename** (`catladder-release.yml` for tagged releases). npm allows one trusted publisher per package, so canary publishes from the main/review workflows still need `NPM_TOKEN`. A set token always wins over OIDC.
58
+ - Disable staging on the component (`env: { stage: false }`) so tagged releases publish directly.
58
59
 
59
60
  ## `pages`
60
61
 
61
- - `type: "pages"` — **required**. Publishes a static site on gitlab pages (not yet supported on the github backend).
62
+ - `type: "pages"` — **required**. Publishes a static site on gitlab pages or github pages. On github the repo's Pages source must be set to "GitHub Actions" once; per-merge-request previews are gitlab-only (github serves one site per repository).
62
63
  - `script: string[]` — **required**; produces the site.
63
64
  - `publishDir?: string` — output directory relative to the repo root, default `"public"`.
64
65
  - `requiresInstall?: boolean` — run the package-manager install (yarn/pnpm) first (`requiresYarnInstall` is a deprecated alias).
@@ -247,6 +247,11 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
247
247
  ? runScript
248
248
  : scripts.add(id, runScript);
249
249
  const artifactPaths = (_g = (_f = job.artifacts) === null || _f === void 0 ? void 0 : _f.paths) !== null && _g !== void 0 ? _g : [];
250
+ // github pages: the built site is handed to the official actions
251
+ // instead of being uploaded as a plain artifact. `deploy-pages`
252
+ // replaces the whole site, so there is no path_prefix equivalent —
253
+ // review previews are rejected upstream (see the pages deploy type).
254
+ const pagesPublishDir = job.pages ? artifactPaths[0] : undefined;
250
255
  const declaredServices = ((_h = job.services) !== null && _h !== void 0 ? _h : []).map((service) => typeof service === "string" ? { name: service } : service);
251
256
  // docker runs natively on github runners, so gitlab's docker-in-docker
252
257
  // services are dropped. The job container stays: the runner mounts the
@@ -292,14 +297,38 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
292
297
  */
293
298
  const referencesSecrets = Object.values(env).some((value) => typeof value === "string" &&
294
299
  (value.includes("${{ secrets.") || value.includes("${{ vars.")));
295
- const githubEnvironment = job.environment && isComponent
300
+ const githubEnvironment = pagesPublishDir
301
+ ? // github requires pages deployments to target the `github-pages`
302
+ // environment, and only the deploy step knows the final url.
303
+ // `secrets-sync-github` derives its targets from the generated
304
+ // workflows, so any secret this job references is synced there.
305
+ {
306
+ name: "github-pages",
307
+ url: "${{ steps.deployment.outputs.page_url }}",
308
+ }
309
+ : job.environment && isComponent
310
+ ? {
311
+ name: `${context.env}-${context.name}`,
312
+ ...(createsJobEnv ? { url: "${{ steps.main.outputs.url }}" } : {}),
313
+ }
314
+ : referencesSecrets
315
+ ? { name: context.env }
316
+ : undefined;
317
+ // declaring `permissions` drops ALL default token grants, so a job
318
+ // that needs any special grant must also re-declare the ordinary ones:
319
+ // `contents: read` for the checkout and `packages: read` to pull its
320
+ // job image from the repo registry.
321
+ const specialPermissions = {
322
+ ...(pagesPublishDir ? { pages: "write", "id-token": "write" } : {}),
323
+ ...(job.idToken ? { "id-token": "write" } : {}),
324
+ };
325
+ const permissions = Object.keys(specialPermissions).length
296
326
  ? {
297
- name: `${context.env}-${context.name}`,
298
- ...(createsJobEnv ? { url: "${{ steps.main.outputs.url }}" } : {}),
327
+ contents: "read",
328
+ ...(resolved.fromRepoRegistry ? { packages: "read" } : {}),
329
+ ...specialPermissions,
299
330
  }
300
- : referencesSecrets
301
- ? { name: context.env }
302
- : undefined;
331
+ : undefined;
303
332
  const githubJob = {
304
333
  name: githubJobName(context, job.name),
305
334
  "runs-on": "ubuntu-latest",
@@ -322,6 +351,7 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
322
351
  : {}),
323
352
  ...(Object.keys(services).length > 0 ? { services } : {}),
324
353
  ...(githubEnvironment ? { environment: githubEnvironment } : {}),
354
+ ...(permissions ? { permissions } : {}),
325
355
  env,
326
356
  steps: [
327
357
  ...(skipCheckout
@@ -341,25 +371,41 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
341
371
  run,
342
372
  shell: "bash",
343
373
  },
344
- ...(uploadProviderIds.has(id) || artifactPaths.length > 0
345
- ? artifactPaths.length > 0
346
- ? [
347
- {
348
- name: "Upload artifacts",
349
- uses: "actions/upload-artifact@v4",
350
- with: {
351
- name: id,
352
- path: artifactPaths.join("\n"),
353
- "if-no-files-found": "warn",
354
- // gitlab artifacts include everything; upload-artifact
355
- // silently drops hidden files by default — which is
356
- // exactly where next.js builds live (.next)
357
- "include-hidden-files": true,
358
- },
359
- },
360
- ]
361
- : []
374
+ ...(pagesPublishDir
375
+ ? [
376
+ {
377
+ name: "Upload pages artifact",
378
+ uses: "actions/upload-pages-artifact@v3",
379
+ with: { path: pagesPublishDir },
380
+ },
381
+ {
382
+ name: "Deploy to github pages",
383
+ id: "deployment",
384
+ uses: "actions/deploy-pages@v4",
385
+ },
386
+ ]
362
387
  : []),
388
+ ...(pagesPublishDir
389
+ ? []
390
+ : uploadProviderIds.has(id) || artifactPaths.length > 0
391
+ ? artifactPaths.length > 0
392
+ ? [
393
+ {
394
+ name: "Upload artifacts",
395
+ uses: "actions/upload-artifact@v4",
396
+ with: {
397
+ name: id,
398
+ path: artifactPaths.join("\n"),
399
+ "if-no-files-found": "warn",
400
+ // gitlab artifacts include everything; upload-artifact
401
+ // silently drops hidden files by default — which is
402
+ // exactly where next.js builds live (.next)
403
+ "include-hidden-files": true,
404
+ },
405
+ },
406
+ ]
407
+ : []
408
+ : []),
363
409
  ],
364
410
  };
365
411
  return [id, githubJob];