@firestartr/cli 1.54.0-snapshot-0 → 1.54.0-snapshot-1

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/build/index.js CHANGED
@@ -356807,6 +356807,10 @@ class SyncerInitializer extends InitializerPatches {
356807
356807
  const provider = helperCTX(ctx).provider;
356808
356808
  return claim.providers[provider].sync || {};
356809
356809
  }
356810
+ function policyInfo(ctx) {
356811
+ const provider = helperCTX(ctx).provider;
356812
+ return claim.providers[provider].policy;
356813
+ }
356810
356814
  return [
356811
356815
  {
356812
356816
  validate(cr) {
@@ -356825,33 +356829,38 @@ class SyncerInitializer extends InitializerPatches {
356825
356829
  if (!PERIOD_VALIDATOR.test(cr.metadata.annotations['firestartr.dev/sync-period'])) {
356826
356830
  throw `${this.identify()}: period incorrect '${cr.metadata.annotations['firestartr.dev/sync-period']}' for ${cr.kind}/${cr.metadata.name}`;
356827
356831
  }
356828
- return true;
356829
356832
  }
356830
356833
  else if (helperHasSyncSchedule(cr)) {
356831
356834
  if (!catalog_common.cron.isValidCron(cr.metadata.annotations[SYNC_SCHED_ANNOTATION])) {
356832
356835
  throw `${this.identify()}: sync-schedule: cron incorrect '${cr.metadata.annotations[SYNC_SCHED_ANNOTATION]}' for ${cr.kind}/${cr.metadata.name}`;
356833
356836
  }
356834
356837
  }
356835
- else {
356836
- return true;
356837
- }
356838
+ return true;
356838
356839
  },
356839
356840
  apply(cr) {
356841
+ cr.metadata.annotations = cr.metadata.annotations || {};
356842
+ // Apply general policy annotation
356843
+ const policy = policyInfo(this);
356844
+ // Default to 'apply' for GitHub resources when no policy is specified
356845
+ const provider = helperCTX(this).provider;
356846
+ const defaultPolicy = provider === 'github' ? 'apply' : undefined;
356847
+ if (policy) {
356848
+ cr.metadata.annotations['firestartr.dev/policy'] = policy;
356849
+ }
356850
+ else if (defaultPolicy) {
356851
+ cr.metadata.annotations['firestartr.dev/policy'] = defaultPolicy;
356852
+ }
356840
356853
  if (syncInfo(this).enabled) {
356841
- cr.metadata.annotations = cr.metadata.annotations || {};
356842
356854
  cr.metadata.annotations['firestartr.dev/sync-enabled'] = 'true';
356843
356855
  if (syncInfo(this).period) {
356844
- cr.metadata.annotations = cr.metadata.annotations || {};
356845
356856
  cr.metadata.annotations['firestartr.dev/sync-period'] =
356846
356857
  syncInfo(this).period;
356847
356858
  }
356848
356859
  if (syncInfo(this).policy) {
356849
- cr.metadata.annotations = cr.metadata.annotations || {};
356850
356860
  cr.metadata.annotations['firestartr.dev/sync-policy'] =
356851
356861
  syncInfo(this).policy;
356852
356862
  }
356853
356863
  if (syncInfo(this).schedule) {
356854
- cr.metadata.annotations = cr.metadata.annotations || {};
356855
356864
  cr.metadata.annotations[SYNC_SCHED_ANNOTATION] =
356856
356865
  syncInfo(this).schedule;
356857
356866
  cr.metadata.annotations[SYNC_SCHED_TIMEZONE_ANNOTATION] =
@@ -357572,6 +357581,76 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357572
357581
  },
357573
357582
  additionalProperties: false,
357574
357583
  },
357584
+ PolicyType: {
357585
+ $id: 'firestartr.dev://common/PolicyType',
357586
+ type: 'string',
357587
+ description: 'Policy for resource management',
357588
+ enum: [
357589
+ 'apply',
357590
+ 'create-only',
357591
+ 'create-update-only',
357592
+ 'full-control',
357593
+ 'observe',
357594
+ 'observe-only',
357595
+ ],
357596
+ },
357597
+ },
357598
+ });
357599
+
357600
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/base/schemas/sync-config.schema.ts
357601
+ /* harmony default export */ const sync_config_schema = ({
357602
+ $id: 'SyncConfig',
357603
+ definitions: {
357604
+ SyncConfig: {
357605
+ $id: 'firestartr.dev://common/SyncConfig',
357606
+ type: 'object',
357607
+ description: 'Sync configuration for resources',
357608
+ properties: {
357609
+ enabled: {
357610
+ type: 'boolean',
357611
+ description: 'Enable periodic sync operations',
357612
+ },
357613
+ period: {
357614
+ type: 'string',
357615
+ pattern: '^[0-9]+[smhd]$',
357616
+ description: 'Sync period (e.g., 1h, 30m, 5s). Must be enabled without schedule.',
357617
+ },
357618
+ schedule: {
357619
+ type: 'string',
357620
+ description: 'Cron schedule for sync operations. Must be enabled without period.',
357621
+ },
357622
+ schedule_timezone: {
357623
+ type: 'string',
357624
+ description: 'Timezone for cron schedule (e.g., UTC, America/New_York)',
357625
+ },
357626
+ policy: {
357627
+ type: 'string',
357628
+ description: 'Policy for sync operations (apply or observe)',
357629
+ },
357630
+ },
357631
+ additionalProperties: false,
357632
+ required: ['enabled'],
357633
+ oneOf: [
357634
+ {
357635
+ required: ['period'],
357636
+ },
357637
+ {
357638
+ required: ['schedule'],
357639
+ },
357640
+ {
357641
+ not: {
357642
+ anyOf: [
357643
+ {
357644
+ required: ['period'],
357645
+ },
357646
+ {
357647
+ required: ['schedule'],
357648
+ },
357649
+ ],
357650
+ },
357651
+ },
357652
+ ],
357653
+ },
357575
357654
  },
357576
357655
  });
357577
357656
 
@@ -357932,6 +358011,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357932
358011
  {
357933
358012
  type: 'object',
357934
358013
  properties: {
358014
+ policy: {
358015
+ $ref: 'firestartr.dev://common/PolicyType',
358016
+ },
357935
358017
  privacy: {
357936
358018
  type: 'string',
357937
358019
  enum: ['closed', 'secret'],
@@ -357942,6 +358024,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357942
358024
  org: {
357943
358025
  type: 'string',
357944
358026
  },
358027
+ sync: {
358028
+ $ref: 'firestartr.dev://common/SyncConfig',
358029
+ },
357945
358030
  },
357946
358031
  required: ['org', 'privacy'],
357947
358032
  },
@@ -357967,6 +358052,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357967
358052
  {
357968
358053
  type: 'object',
357969
358054
  properties: {
358055
+ policy: {
358056
+ $ref: 'firestartr.dev://common/PolicyType',
358057
+ },
357970
358058
  role: {
357971
358059
  type: 'string',
357972
358060
  enum: ['admin', 'member'],
@@ -357974,6 +358062,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357974
358062
  org: {
357975
358063
  type: 'string',
357976
358064
  },
358065
+ sync: {
358066
+ $ref: 'firestartr.dev://common/SyncConfig',
358067
+ },
357977
358068
  },
357978
358069
  required: ['org', 'role'],
357979
358070
  },
@@ -357998,6 +358089,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357998
358089
  {
357999
358090
  type: 'object',
358000
358091
  properties: {
358092
+ policy: {
358093
+ $ref: 'firestartr.dev://common/PolicyType',
358094
+ },
358001
358095
  org: {
358002
358096
  type: 'string',
358003
358097
  description: 'The github organization name',
@@ -358006,6 +358100,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358006
358100
  type: 'string',
358007
358101
  enum: ['private', 'public', 'internal'],
358008
358102
  },
358103
+ sync: {
358104
+ $ref: 'firestartr.dev://common/SyncConfig',
358105
+ },
358009
358106
  features: {
358010
358107
  type: 'array',
358011
358108
  items: {
@@ -358041,6 +358138,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358041
358138
  {
358042
358139
  type: 'object',
358043
358140
  properties: {
358141
+ policy: {
358142
+ $ref: 'firestartr.dev://common/PolicyType',
358143
+ },
358044
358144
  orgName: {
358045
358145
  type: 'string',
358046
358146
  description: 'Organization name on GitHub',
@@ -358074,6 +358174,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358074
358174
  },
358075
358175
  required: ['url', 'contentType', 'events', 'secretRef'],
358076
358176
  },
358177
+ sync: {
358178
+ $ref: 'firestartr.dev://common/SyncConfig',
358179
+ },
358077
358180
  },
358078
358181
  required: ['orgName', 'webhook'],
358079
358182
  },
@@ -358245,15 +358348,7 @@ const GithubSchemas = [
358245
358348
  type: 'object',
358246
358349
  properties: {
358247
358350
  policy: {
358248
- type: 'string',
358249
- enum: [
358250
- 'apply',
358251
- 'create-only',
358252
- 'create-update-only',
358253
- 'full-control',
358254
- 'observe',
358255
- 'observe-only',
358256
- ],
358351
+ $ref: 'firestartr.dev://common/PolicyType',
358257
358352
  },
358258
358353
  name: {
358259
358354
  type: 'string',
@@ -358263,47 +358358,7 @@ const GithubSchemas = [
358263
358358
  enum: ['remote', 'inline', 'Remote', 'Inline'],
358264
358359
  },
358265
358360
  sync: {
358266
- type: 'object',
358267
- properties: {
358268
- enabled: {
358269
- type: 'boolean',
358270
- },
358271
- period: {
358272
- type: 'string',
358273
- pattern: '^[0-9]+[smhd]$',
358274
- },
358275
- schedule: {
358276
- type: 'string',
358277
- },
358278
- schedule_timezone: {
358279
- type: 'string',
358280
- },
358281
- policy: {
358282
- type: 'string',
358283
- },
358284
- },
358285
- additionalProperties: false,
358286
- required: ['enabled'],
358287
- oneOf: [
358288
- {
358289
- required: ['period'],
358290
- },
358291
- {
358292
- required: ['schedule'],
358293
- },
358294
- {
358295
- not: {
358296
- anyOf: [
358297
- {
358298
- required: ['period'],
358299
- },
358300
- {
358301
- required: ['schedule'],
358302
- },
358303
- ],
358304
- },
358305
- },
358306
- ],
358361
+ $ref: 'firestartr.dev://common/SyncConfig',
358307
358362
  },
358308
358363
  valuesSchema: {
358309
358364
  type: 'string',
@@ -358604,10 +358659,12 @@ const SecretsSchemas = [external_secrets_schema];
358604
358659
 
358605
358660
 
358606
358661
 
358662
+
358607
358663
  const schemas = {
358608
358664
  root: root_schema,
358609
358665
  schemas: [
358610
358666
  common_meta_schema,
358667
+ sync_config_schema,
358611
358668
  group_schema,
358612
358669
  user_schema,
358613
358670
  component_schema,
@@ -359560,7 +359617,6 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
359560
359617
 
359561
359618
 
359562
359619
 
359563
-
359564
359620
  function validate_validate(featurePath) {
359565
359621
  __validateDirStructure(featurePath);
359566
359622
  const configData = __validateFeatureConfig(featurePath);
@@ -359575,17 +359631,6 @@ function __validateDirStructure(featurePath) {
359575
359631
  if (!isFile(external_path_.join(featurePath, 'config.yaml'))) {
359576
359632
  throw `Feature: ${featurePath}/config.yaml not found or not a file`;
359577
359633
  }
359578
- if (!isDirectory(external_path_.join(featurePath, 'templates/docs'))) {
359579
- throw `Feature: ${featurePath}templates/docs not a directory`;
359580
- }
359581
- if (!__validateDocsHasReadmes(featurePath)) {
359582
- throw `Feature: ${featurePath}: should have at least one .md file in the templates/docs`;
359583
- }
359584
- }
359585
- function __validateDocsHasReadmes(featurePath) {
359586
- const dirEntries = external_fs_default().readdirSync(external_path_.join(featurePath, 'templates/docs'));
359587
- return (dirEntries.some((entryFile) => !isDirectory(external_path_.join(featurePath, '/templates/docs', entryFile)) &&
359588
- entryFile.endsWith('.md')) !== undefined);
359589
359634
  }
359590
359635
  function __validateFeatureConfig(featurePath) {
359591
359636
  try {
@@ -360641,7 +360686,6 @@ function buildExpectedOutput(config, renderDir) {
360641
360686
  localPath: external_node_path_.join(renderDir, f.dest),
360642
360687
  repoPath: f.dest,
360643
360688
  userManaged: f.user_managed,
360644
- targetBranch: f.target_branch ?? '',
360645
360689
  }));
360646
360690
  return {
360647
360691
  files,
@@ -364105,8 +364149,12 @@ class FeatureRepoChart extends BaseGithubChart {
364105
364149
  const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
364106
364150
  'claim-ref',
364107
364151
  'revision',
364152
+ 'policy',
364153
+ 'sync-policy',
364108
364154
  'sync-enabled',
364109
364155
  'sync-period',
364156
+ 'sync-schedule',
364157
+ 'sync-schedule-timezone',
364110
364158
  ]);
364111
364159
  cr.metadata.annotations = {
364112
364160
  ...cr.metadata.annotations,
@@ -364181,8 +364229,12 @@ class RepoSecretsSectionChart extends BaseGithubChart {
364181
364229
  const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
364182
364230
  'claim-ref',
364183
364231
  'revision',
364232
+ 'policy',
364233
+ 'sync-policy',
364184
364234
  'sync-enabled',
364185
364235
  'sync-period',
364236
+ 'sync-schedule',
364237
+ 'sync-schedule-timezone',
364186
364238
  ]);
364187
364239
  cr.metadata.annotations = {
364188
364240
  ...cr.metadata.annotations,
@@ -370480,10 +370532,15 @@ class Resource {
370480
370532
  this.set('operation', operation);
370481
370533
  this.set('deps', deps);
370482
370534
  }
370483
- async run() {
370535
+ async run(options) {
370484
370536
  await this.preprocess();
370485
370537
  await this.synth();
370486
- await this.runTerraform();
370538
+ if (options?.planOnly) {
370539
+ await this.runTerraformPlanOnly();
370540
+ }
370541
+ else {
370542
+ await this.runTerraform();
370543
+ }
370487
370544
  await this.postprocess();
370488
370545
  if (this.logStream) {
370489
370546
  this.logStream.end();
@@ -370508,6 +370565,13 @@ class Resource {
370508
370565
  log(msg) {
370509
370566
  this.logFn(msg);
370510
370567
  }
370568
+ async runTerraformPlanOnly() {
370569
+ await this.onTFStreaming();
370570
+ let output = '';
370571
+ output += await terraformInit(this.get('main_artifact'), this.logStream);
370572
+ output += await terraformPlan(this.get('main_artifact'), this.logStream);
370573
+ this.set('output', output);
370574
+ }
370511
370575
  async runTerraform() {
370512
370576
  await this.onTFStreaming();
370513
370577
  let output = '';
@@ -370895,7 +370959,7 @@ async function runProvisioner(data, opts) {
370895
370959
  if ('logStreamCallbacksTF' in opts) {
370896
370960
  resource.setTFStreamLogs(opts['logStreamCallbacksTF']);
370897
370961
  }
370898
- await resource.run();
370962
+ await resource.run({ planOnly: opts.planOnly });
370899
370963
  return resource;
370900
370964
  }
370901
370965
  function createInstanceOf(entity, op, deps) {
@@ -371177,6 +371241,52 @@ function helperCreateCheckRunName(cmd, item) {
371177
371241
  return `${item.kind} - ${cmd}`;
371178
371242
  }
371179
371243
 
371244
+ ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
371245
+ const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
371246
+ function replaceConfigSecrets(config, secrets) {
371247
+ for (const key in config) {
371248
+ if (typeof config[key] === 'object' && config[key] !== null) {
371249
+ // If the property is an object, call this function recursively
371250
+ replaceConfigSecrets(config[key], secrets);
371251
+ }
371252
+ else if (typeof config[key] === 'string') {
371253
+ // If the property is a string and its value is equal to secrets.something,
371254
+ // replace the value with the value of the 'something' key in the secrets object
371255
+ config[key] = config[key].replace(secretRegex, (_, group1) => {
371256
+ if (!secrets[group1]) {
371257
+ throw new Error(`Secret ${group1} not found in secrets`);
371258
+ }
371259
+ return secrets[group1];
371260
+ });
371261
+ }
371262
+ }
371263
+ return config;
371264
+ }
371265
+ function replaceInlineSecrets(inline, secrets) {
371266
+ if (typeof inline !== 'string' || !inline)
371267
+ return inline;
371268
+ let result = inline;
371269
+ result = result.replace(secretRegex, (_, group1) => {
371270
+ if (!secrets[group1]) {
371271
+ throw new Error(`Secret ${group1} not found in secrets`);
371272
+ }
371273
+ return secrets[group1];
371274
+ });
371275
+ return result;
371276
+ }
371277
+ /**
371278
+ * Retrieves a policy annotation value from a custom resource
371279
+ * @param item - The CR to get the policy from
371280
+ * @param annotation - The annotation key to retrieve
371281
+ * @returns The policy value, or undefined if not set
371282
+ */
371283
+ function getPolicy(item, annotation) {
371284
+ const policy = item.metadata.annotations && item.metadata.annotations[annotation];
371285
+ if (policy)
371286
+ return policy;
371287
+ return undefined;
371288
+ }
371289
+
371180
371290
  ;// CONCATENATED MODULE: ../operator/src/utils/operationErrorMessages.ts
371181
371291
  const APPLY_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraform apply operation.';
371182
371292
  const DESTROY_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraform destroy operation.';
@@ -371192,9 +371302,17 @@ const PLAN_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraf
371192
371302
 
371193
371303
 
371194
371304
 
371305
+ const cdktf_LAST_STATE_PR_ANNOTATION = 'firestartr.dev/last-state-pr';
371306
+
371195
371307
  function processOperation(item, op, handler) {
371196
371308
  operator_src_logger.info(`Processing operation ${op} on ${item.kind}/${item.metadata?.name}`);
371197
371309
  try {
371310
+ const policy = getPolicy(item, 'firestartr.dev/policy');
371311
+ // If general policy is observe/observe-only, route to observe mode instead of apply
371312
+ if (!policy || policy === 'observe' || policy === 'observe-only') {
371313
+ operator_src_logger.info(`Policy is '${policy || 'not set (default)'}', routing to observe mode`);
371314
+ return cdktf_observe(item, op, handler);
371315
+ }
371198
371316
  switch (op) {
371199
371317
  case OperationType.UPDATED:
371200
371318
  return updated(item, op, handler);
@@ -371219,6 +371337,11 @@ function processOperation(item, op, handler) {
371219
371337
  throw e;
371220
371338
  }
371221
371339
  }
371340
+ async function* cdktf_observe(item, op, handler) {
371341
+ for await (const transition of doPlan(item, op, handler)) {
371342
+ yield transition;
371343
+ }
371344
+ }
371222
371345
  async function* created(item, op, handler) {
371223
371346
  for await (const transition of doApply(item, op, handler)) {
371224
371347
  yield transition;
@@ -371254,8 +371377,18 @@ async function* sync(item, op, handler) {
371254
371377
  status: 'False',
371255
371378
  message: 'Synth CDKTF',
371256
371379
  };
371257
- for await (const transition of doApply(item, op, handler)) {
371258
- yield transition;
371380
+ const syncPolicy = getPolicy(item, 'firestartr.dev/sync-policy');
371381
+ if (syncPolicy === 'apply') {
371382
+ operator_src_logger.info(`SYNC OPERATION: applying item ${item.metadata.name} with sync-policy=${syncPolicy}`);
371383
+ for await (const transition of doApply(item, op, handler)) {
371384
+ yield transition;
371385
+ }
371386
+ }
371387
+ else {
371388
+ operator_src_logger.info(`SYNC OPERATION: planning item ${item.metadata.name} with sync-policy=${syncPolicy || 'default (observe)'}`);
371389
+ for await (const transition of doPlan(item, op, handler)) {
371390
+ yield transition;
371391
+ }
371259
371392
  }
371260
371393
  yield {
371261
371394
  item,
@@ -371295,15 +371428,14 @@ async function* markedToDeletion(item, op, handler) {
371295
371428
  message: 'Destroying process started',
371296
371429
  };
371297
371430
  const deps = await handler.resolveReferences();
371298
- const annotation = 'firestartr.dev/last-state-pr';
371299
- const statePr = item?.metadata?.annotations?.[annotation];
371431
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371300
371432
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371301
371433
  if (!hasStatePr) {
371302
371434
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371303
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371435
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371304
371436
  }
371305
371437
  else {
371306
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371438
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371307
371439
  }
371308
371440
  const destroyOutput = await provisioner.runProvisioner({
371309
371441
  mainCr: item,
@@ -371338,7 +371470,7 @@ async function* markedToDeletion(item, op, handler) {
371338
371470
  };
371339
371471
  await handler.finalize(handler.pluralKind, item.metadata.namespace, item, 'firestartr.dev/finalizer');
371340
371472
  await handler.writeTerraformOutputInTfResult(item, output);
371341
- if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
371473
+ if (item.metadata.annotations[cdktf_LAST_STATE_PR_ANNOTATION] || false) {
371342
371474
  await addDestroyCommitStatus(item, 'success', 'Destroy operation completed', `Terraform Destroy ${item.metadata.name}`);
371343
371475
  }
371344
371476
  void handler.success();
@@ -371425,15 +371557,14 @@ async function* doApply(item, op, handler) {
371425
371557
  }
371426
371558
  const deps = await handler.resolveReferences();
371427
371559
  operator_src_logger.info(`Item ${item.metadata.name} has the following dependencies: ${deps}`);
371428
- const annotation = 'firestartr.dev/last-state-pr';
371429
- const statePr = item?.metadata?.annotations?.[annotation];
371560
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371430
371561
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371431
371562
  if (!hasStatePr) {
371432
371563
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371433
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371564
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371434
371565
  }
371435
371566
  else {
371436
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371567
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371437
371568
  }
371438
371569
  const applyOutput = await provisioner.runProvisioner({
371439
371570
  mainCr: item,
@@ -371534,6 +371665,220 @@ async function* doApply(item, op, handler) {
371534
371665
  }
371535
371666
  }
371536
371667
  }
371668
+ async function* doPlan(item, op, handler) {
371669
+ let checkRunCtl;
371670
+ try {
371671
+ cleanTerraformState();
371672
+ yield {
371673
+ item,
371674
+ reason: op,
371675
+ type: 'PLANNING',
371676
+ status: 'True',
371677
+ message: 'Planning process started',
371678
+ };
371679
+ const deps = await handler.resolveReferences();
371680
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371681
+ const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371682
+ if (!hasStatePr) {
371683
+ operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371684
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs for plan.`);
371685
+ }
371686
+ else {
371687
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371688
+ await addPlanStatusCheck(statePr, 'CDKTF plan in progress...');
371689
+ }
371690
+ // Run provisioner in plan-only mode
371691
+ const planResult = await provisioner.runProvisioner({ mainCr: item, deps }, {
371692
+ planOnly: true,
371693
+ delete: 'deletionTimestamp' in item.metadata,
371694
+ ...(hasStatePr
371695
+ ? {
371696
+ logStreamCallbacksCDKTF: {
371697
+ prepare: async () => {
371698
+ checkRunCtl = await GHCheckRun('synth', item);
371699
+ return checkRunCtl;
371700
+ },
371701
+ },
371702
+ logStreamCallbacksTF: {
371703
+ prepare: async () => {
371704
+ checkRunCtl = await GHCheckRun('plan', item);
371705
+ return checkRunCtl;
371706
+ },
371707
+ },
371708
+ }
371709
+ : {}),
371710
+ });
371711
+ const planOutput = planResult?.output || '';
371712
+ // Parse terraform plan output to detect changes
371713
+ // Handles multiple Terraform output formats and versions
371714
+ const hasChanges = detectPlanChanges(planOutput);
371715
+ if (hasChanges) {
371716
+ yield {
371717
+ item,
371718
+ reason: op,
371719
+ type: 'OUT_OF_SYNC',
371720
+ status: 'True',
371721
+ message: 'Plan has changes',
371722
+ };
371723
+ yield {
371724
+ item,
371725
+ reason: op,
371726
+ type: 'PROVISIONED',
371727
+ status: 'False',
371728
+ message: 'Plan has changes',
371729
+ };
371730
+ }
371731
+ else {
371732
+ yield {
371733
+ item,
371734
+ reason: op,
371735
+ type: 'OUT_OF_SYNC',
371736
+ status: 'False',
371737
+ message: 'Plan has no changes',
371738
+ };
371739
+ yield {
371740
+ item,
371741
+ reason: op,
371742
+ type: 'PROVISIONED',
371743
+ status: 'True',
371744
+ message: 'Plan has no changes',
371745
+ };
371746
+ }
371747
+ // Store plan details for later reference
371748
+ yield {
371749
+ item,
371750
+ reason: op,
371751
+ type: 'LAST_PLAN_DETAILS',
371752
+ status: 'Unknown',
371753
+ message: planOutput,
371754
+ };
371755
+ yield {
371756
+ item,
371757
+ reason: op,
371758
+ type: 'PLANNING',
371759
+ status: 'False',
371760
+ message: 'Planning process finished',
371761
+ };
371762
+ if (hasStatePr) {
371763
+ await addPlanStatusCheck(statePr, hasChanges ? 'Plan has changes' : 'Plan has no changes', 'completed');
371764
+ }
371765
+ }
371766
+ catch (e) {
371767
+ operator_src_logger.error(`CDKTF plan failed: ${e}`);
371768
+ if (checkRunCtl) {
371769
+ checkRunCtl.fnOnError(e);
371770
+ }
371771
+ yield {
371772
+ item,
371773
+ reason: op,
371774
+ type: 'ERROR',
371775
+ status: 'True',
371776
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
371777
+ };
371778
+ yield {
371779
+ item,
371780
+ reason: op,
371781
+ type: 'PLANNING',
371782
+ status: 'False',
371783
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
371784
+ };
371785
+ yield {
371786
+ item,
371787
+ reason: op,
371788
+ type: 'PROVISIONED',
371789
+ status: 'False',
371790
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
371791
+ };
371792
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371793
+ if (statePr) {
371794
+ const summaryText = tryCreateErrorSummary('CDKTF Plan failed', e);
371795
+ await addPlanStatusCheck(statePr, summaryText, 'completed', true);
371796
+ }
371797
+ await handler.writeTerraformOutputInTfResult(item, e);
371798
+ void handler.error();
371799
+ }
371800
+ }
371801
+ /**
371802
+ * Detects if a Terraform plan output contains changes
371803
+ * Handles multiple Terraform versions and output formats
371804
+ * @param planOutput - The text output from terraform plan
371805
+ * @returns true if changes are detected, false otherwise
371806
+ */
371807
+ function detectPlanChanges(planOutput) {
371808
+ if (!planOutput || planOutput.trim().length === 0) {
371809
+ return false;
371810
+ }
371811
+ // Pattern 1: "Plan: X to add, Y to change, Z to destroy"
371812
+ // Matches: "Plan: 1 to add, 0 to change, 0 to destroy"
371813
+ const planPattern = /plan:\s*(\d+)\s+to\s+add,\s*(\d+)\s+to\s+change,\s*(\d+)\s+to\s+destroy/i;
371814
+ const planMatch = planOutput.match(planPattern);
371815
+ if (planMatch) {
371816
+ const add = Number(planMatch[1]);
371817
+ const change = Number(planMatch[2]);
371818
+ const destroy = Number(planMatch[3]);
371819
+ if (add > 0 || change > 0 || destroy > 0) {
371820
+ return true;
371821
+ }
371822
+ // Explicitly found "Plan:" with 0/0/0 - no changes
371823
+ return false;
371824
+ }
371825
+ // Pattern 2: Individual change indicators
371826
+ // Handles variations like "1 to add", "2 to change", "3 to destroy"
371827
+ const hasAdditions = /\d+\s+to\s+add/i.test(planOutput);
371828
+ const hasChanges = /\d+\s+to\s+change/i.test(planOutput);
371829
+ const hasDestructions = /\d+\s+to\s+destroy/i.test(planOutput);
371830
+ const hasImports = /\d+\s+to\s+import/i.test(planOutput);
371831
+ if (hasAdditions || hasChanges || hasDestructions || hasImports) {
371832
+ return true;
371833
+ }
371834
+ // Pattern 3: Resource-level change indicators
371835
+ // Matches: "# resource will be created", "# resource will be updated", etc.
371836
+ const resourceChangePatterns = [
371837
+ /will\s+be\s+(created|destroyed|updated|replaced)/i,
371838
+ /must\s+be\s+(created|destroyed|updated|replaced)/i,
371839
+ /#.*\s+(create|destroy|update|replace)/i,
371840
+ ];
371841
+ for (const pattern of resourceChangePatterns) {
371842
+ if (pattern.test(planOutput)) {
371843
+ return true;
371844
+ }
371845
+ }
371846
+ // Pattern 4: Action symbols in plan output
371847
+ // Terraform uses symbols like +, -, ~, -/+ to indicate changes
371848
+ const actionSymbols = [
371849
+ /^\s*[+]\s+/m,
371850
+ /^\s*[-]\s+/m,
371851
+ /^\s*[~]\s+/m,
371852
+ /^\s*[-][/][+]\s+/m, // Replace
371853
+ ];
371854
+ for (const pattern of actionSymbols) {
371855
+ if (pattern.test(planOutput)) {
371856
+ return true;
371857
+ }
371858
+ }
371859
+ // Pattern 5: No changes messages (inverse check)
371860
+ const noChangesPatterns = [
371861
+ /no\s+changes/i,
371862
+ /infrastructure\s+is\s+up[-\s]to[-\s]date/i,
371863
+ /your\s+infrastructure\s+matches\s+the\s+configuration/i,
371864
+ /0\s+to\s+add,\s*0\s+to\s+change,\s*0\s+to\s+destroy/i,
371865
+ ];
371866
+ for (const pattern of noChangesPatterns) {
371867
+ if (pattern.test(planOutput)) {
371868
+ return false;
371869
+ }
371870
+ }
371871
+ // If we find "Plan:" keyword but couldn't parse it, log a warning and err on the side of caution
371872
+ if (planOutput.toLowerCase().includes('plan:')) {
371873
+ operator_src_logger.warn('Found "Plan:" in output but could not parse change counts. ' +
371874
+ 'Raw output excerpt (first 500 chars):\n' +
371875
+ planOutput.slice(0, 500) +
371876
+ '\nErring on the side of caution and assuming changes.');
371877
+ return true;
371878
+ }
371879
+ // Default: assume no changes if we can't detect any
371880
+ return false;
371881
+ }
371537
371882
  function cleanTerraformState() {
371538
371883
  external_fs_.rmSync('/library/packages/provisioner/cdktf.out', {
371539
371884
  recursive: true,
@@ -372533,40 +372878,6 @@ function tf_checkrun_helperCreateCheckRunName(cmd) {
372533
372878
  return `TFWorkspace - ${cmd}`;
372534
372879
  }
372535
372880
 
372536
- ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
372537
- const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
372538
- function replaceConfigSecrets(config, secrets) {
372539
- for (const key in config) {
372540
- if (typeof config[key] === 'object' && config[key] !== null) {
372541
- // If the property is an object, call this function recursively
372542
- replaceConfigSecrets(config[key], secrets);
372543
- }
372544
- else if (typeof config[key] === 'string') {
372545
- // If the property is a string and its value is equal to secrets.something,
372546
- // replace the value with the value of the 'something' key in the secrets object
372547
- config[key] = config[key].replace(secretRegex, (_, group1) => {
372548
- if (!secrets[group1]) {
372549
- throw new Error(`Secret ${group1} not found in secrets`);
372550
- }
372551
- return secrets[group1];
372552
- });
372553
- }
372554
- }
372555
- return config;
372556
- }
372557
- function replaceInlineSecrets(inline, secrets) {
372558
- if (typeof inline !== 'string' || !inline)
372559
- return inline;
372560
- let result = inline;
372561
- result = result.replace(secretRegex, (_, group1) => {
372562
- if (!secrets[group1]) {
372563
- throw new Error(`Secret ${group1} not found in secrets`);
372564
- }
372565
- return secrets[group1];
372566
- });
372567
- return result;
372568
- }
372569
-
372570
372881
  ;// CONCATENATED MODULE: ../operator/src/tfworkspaces/process-operation.ts
372571
372882
 
372572
372883
 
@@ -372764,11 +373075,6 @@ async function* process_operation_renamed(item, op, handler) {
372764
373075
  yield transition;
372765
373076
  }
372766
373077
  }
372767
- function getPolicy(item, annotation) {
372768
- const policy = item.metadata.annotations && item.metadata.annotations[annotation];
372769
- if (policy)
372770
- return policy;
372771
- }
372772
373078
  async function* process_operation_updated(item, op, handler) {
372773
373079
  for await (const transition of process_operation_doApply(item, op, handler)) {
372774
373080
  yield transition;
@@ -373425,7 +373731,7 @@ const processOperationPlan_TF_PROJECTS_PATH = '/tmp/tfworkspaces';
373425
373731
  function processOperationPlan(item, op, handler) {
373426
373732
  try {
373427
373733
  processOperationPlan_clearLocalTfProjects();
373428
- const policy = processOperationPlan_getPolicy(item);
373734
+ const policy = getPolicy(item, 'firestartr.dev/policy');
373429
373735
  if (policy === 'observe' || policy === 'apply') {
373430
373736
  return processOperationPlan_plan(item, op, handler);
373431
373737
  }
@@ -373884,12 +374190,6 @@ function processOperationPlan_getErrorOutputMessage(cr, key, ref) {
373884
374190
  throw new Error(`❌ Source ${cr.spec.source} not supported`);
373885
374191
  }
373886
374192
  }
373887
- function processOperationPlan_getPolicy(item) {
373888
- const policy = item.metadata.annotations &&
373889
- item.metadata.annotations['firestartr.dev/policy'];
373890
- if (policy)
373891
- return policy;
373892
- }
373893
374193
 
373894
374194
  ;// CONCATENATED MODULE: ../operator/src/ctx.ts
373895
374195
  class Ctx {