@firestartr/cli 1.53.0-snapshot-7 → 1.53.0-snapshot-8

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) {
@@ -356837,21 +356841,22 @@ class SyncerInitializer extends InitializerPatches {
356837
356841
  }
356838
356842
  },
356839
356843
  apply(cr) {
356844
+ cr.metadata.annotations = cr.metadata.annotations || {};
356845
+ // Apply general policy annotation
356846
+ if (policyInfo(this)) {
356847
+ cr.metadata.annotations['firestartr.dev/policy'] = policyInfo(this);
356848
+ }
356840
356849
  if (syncInfo(this).enabled) {
356841
- cr.metadata.annotations = cr.metadata.annotations || {};
356842
356850
  cr.metadata.annotations['firestartr.dev/sync-enabled'] = 'true';
356843
356851
  if (syncInfo(this).period) {
356844
- cr.metadata.annotations = cr.metadata.annotations || {};
356845
356852
  cr.metadata.annotations['firestartr.dev/sync-period'] =
356846
356853
  syncInfo(this).period;
356847
356854
  }
356848
356855
  if (syncInfo(this).policy) {
356849
- cr.metadata.annotations = cr.metadata.annotations || {};
356850
356856
  cr.metadata.annotations['firestartr.dev/sync-policy'] =
356851
356857
  syncInfo(this).policy;
356852
356858
  }
356853
356859
  if (syncInfo(this).schedule) {
356854
- cr.metadata.annotations = cr.metadata.annotations || {};
356855
356860
  cr.metadata.annotations[SYNC_SCHED_ANNOTATION] =
356856
356861
  syncInfo(this).schedule;
356857
356862
  cr.metadata.annotations[SYNC_SCHED_TIMEZONE_ANNOTATION] =
@@ -357572,6 +357577,69 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357572
357577
  },
357573
357578
  additionalProperties: false,
357574
357579
  },
357580
+ PolicyType: {
357581
+ $id: 'firestartr.dev://common/PolicyType',
357582
+ type: 'string',
357583
+ description: 'Policy for resource management',
357584
+ enum: [
357585
+ 'apply',
357586
+ 'create-only',
357587
+ 'create-update-only',
357588
+ 'full-control',
357589
+ 'observe',
357590
+ 'observe-only',
357591
+ ],
357592
+ },
357593
+ SyncConfig: {
357594
+ $id: 'firestartr.dev://common/SyncConfig',
357595
+ type: 'object',
357596
+ description: 'Sync configuration for resources',
357597
+ properties: {
357598
+ enabled: {
357599
+ type: 'boolean',
357600
+ description: 'Enable periodic sync operations',
357601
+ },
357602
+ period: {
357603
+ type: 'string',
357604
+ pattern: '^[0-9]+[smhd]$',
357605
+ description: 'Sync period (e.g., 1h, 30m, 5s). Must be enabled without schedule.',
357606
+ },
357607
+ schedule: {
357608
+ type: 'string',
357609
+ description: 'Cron schedule for sync operations. Must be enabled without period.',
357610
+ },
357611
+ schedule_timezone: {
357612
+ type: 'string',
357613
+ description: 'Timezone for cron schedule (e.g., UTC, America/New_York)',
357614
+ },
357615
+ policy: {
357616
+ type: 'string',
357617
+ description: 'Policy for sync operations (apply or observe)',
357618
+ },
357619
+ },
357620
+ additionalProperties: false,
357621
+ required: ['enabled'],
357622
+ oneOf: [
357623
+ {
357624
+ required: ['period'],
357625
+ },
357626
+ {
357627
+ required: ['schedule'],
357628
+ },
357629
+ {
357630
+ not: {
357631
+ anyOf: [
357632
+ {
357633
+ required: ['period'],
357634
+ },
357635
+ {
357636
+ required: ['schedule'],
357637
+ },
357638
+ ],
357639
+ },
357640
+ },
357641
+ ],
357642
+ },
357575
357643
  },
357576
357644
  });
357577
357645
 
@@ -357932,6 +358000,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357932
358000
  {
357933
358001
  type: 'object',
357934
358002
  properties: {
358003
+ policy: {
358004
+ $ref: 'firestartr.dev://common/PolicyType',
358005
+ },
357935
358006
  privacy: {
357936
358007
  type: 'string',
357937
358008
  enum: ['closed', 'secret'],
@@ -357942,6 +358013,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357942
358013
  org: {
357943
358014
  type: 'string',
357944
358015
  },
358016
+ sync: {
358017
+ $ref: 'firestartr.dev://common/SyncConfig',
358018
+ },
357945
358019
  },
357946
358020
  required: ['org', 'privacy'],
357947
358021
  },
@@ -357967,6 +358041,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357967
358041
  {
357968
358042
  type: 'object',
357969
358043
  properties: {
358044
+ policy: {
358045
+ $ref: 'firestartr.dev://common/PolicyType',
358046
+ },
357970
358047
  role: {
357971
358048
  type: 'string',
357972
358049
  enum: ['admin', 'member'],
@@ -357974,6 +358051,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357974
358051
  org: {
357975
358052
  type: 'string',
357976
358053
  },
358054
+ sync: {
358055
+ $ref: 'firestartr.dev://common/SyncConfig',
358056
+ },
357977
358057
  },
357978
358058
  required: ['org', 'role'],
357979
358059
  },
@@ -357998,6 +358078,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357998
358078
  {
357999
358079
  type: 'object',
358000
358080
  properties: {
358081
+ policy: {
358082
+ $ref: 'firestartr.dev://common/PolicyType',
358083
+ },
358001
358084
  org: {
358002
358085
  type: 'string',
358003
358086
  description: 'The github organization name',
@@ -358006,6 +358089,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358006
358089
  type: 'string',
358007
358090
  enum: ['private', 'public', 'internal'],
358008
358091
  },
358092
+ sync: {
358093
+ $ref: 'firestartr.dev://common/SyncConfig',
358094
+ },
358009
358095
  features: {
358010
358096
  type: 'array',
358011
358097
  items: {
@@ -358041,6 +358127,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358041
358127
  {
358042
358128
  type: 'object',
358043
358129
  properties: {
358130
+ policy: {
358131
+ $ref: 'firestartr.dev://common/PolicyType',
358132
+ },
358044
358133
  orgName: {
358045
358134
  type: 'string',
358046
358135
  description: 'Organization name on GitHub',
@@ -358074,6 +358163,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358074
358163
  },
358075
358164
  required: ['url', 'contentType', 'events', 'secretRef'],
358076
358165
  },
358166
+ sync: {
358167
+ $ref: 'firestartr.dev://common/SyncConfig',
358168
+ },
358077
358169
  },
358078
358170
  required: ['orgName', 'webhook'],
358079
358171
  },
@@ -358245,15 +358337,7 @@ const GithubSchemas = [
358245
358337
  type: 'object',
358246
358338
  properties: {
358247
358339
  policy: {
358248
- type: 'string',
358249
- enum: [
358250
- 'apply',
358251
- 'create-only',
358252
- 'create-update-only',
358253
- 'full-control',
358254
- 'observe',
358255
- 'observe-only',
358256
- ],
358340
+ $ref: 'firestartr.dev://common/PolicyType',
358257
358341
  },
358258
358342
  name: {
358259
358343
  type: 'string',
@@ -358263,47 +358347,7 @@ const GithubSchemas = [
358263
358347
  enum: ['remote', 'inline', 'Remote', 'Inline'],
358264
358348
  },
358265
358349
  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
- ],
358350
+ $ref: 'firestartr.dev://common/SyncConfig',
358307
358351
  },
358308
358352
  valuesSchema: {
358309
358353
  type: 'string',
@@ -364067,8 +364111,12 @@ class FeatureRepoChart extends BaseGithubChart {
364067
364111
  const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
364068
364112
  'claim-ref',
364069
364113
  'revision',
364114
+ 'policy',
364115
+ 'sync-policy',
364070
364116
  'sync-enabled',
364071
364117
  'sync-period',
364118
+ 'sync-schedule',
364119
+ 'sync-schedule-timezone',
364072
364120
  ]);
364073
364121
  cr.metadata.annotations = {
364074
364122
  ...cr.metadata.annotations,
@@ -364141,8 +364189,12 @@ class RepoSecretsSectionChart extends BaseGithubChart {
364141
364189
  const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
364142
364190
  'claim-ref',
364143
364191
  'revision',
364192
+ 'policy',
364193
+ 'sync-policy',
364144
364194
  'sync-enabled',
364145
364195
  'sync-period',
364196
+ 'sync-schedule',
364197
+ 'sync-schedule-timezone',
364146
364198
  ]);
364147
364199
  cr.metadata.annotations = {
364148
364200
  ...cr.metadata.annotations,
@@ -370410,10 +370462,15 @@ class Resource {
370410
370462
  this.set('operation', operation);
370411
370463
  this.set('deps', deps);
370412
370464
  }
370413
- async run() {
370465
+ async run(options) {
370414
370466
  await this.preprocess();
370415
370467
  await this.synth();
370416
- await this.runTerraform();
370468
+ if (options?.planOnly) {
370469
+ await this.runTerraformPlanOnly();
370470
+ }
370471
+ else {
370472
+ await this.runTerraform();
370473
+ }
370417
370474
  await this.postprocess();
370418
370475
  if (this.logStream) {
370419
370476
  this.logStream.end();
@@ -370438,6 +370495,13 @@ class Resource {
370438
370495
  log(msg) {
370439
370496
  this.logFn(msg);
370440
370497
  }
370498
+ async runTerraformPlanOnly() {
370499
+ await this.onTFStreaming();
370500
+ let output = '';
370501
+ output += await terraformInit(this.get('main_artifact'), this.logStream);
370502
+ output += await terraformPlan(this.get('main_artifact'), this.logStream);
370503
+ this.set('output', output);
370504
+ }
370441
370505
  async runTerraform() {
370442
370506
  await this.onTFStreaming();
370443
370507
  let output = '';
@@ -370825,7 +370889,7 @@ async function runProvisioner(data, opts) {
370825
370889
  if ('logStreamCallbacksTF' in opts) {
370826
370890
  resource.setTFStreamLogs(opts['logStreamCallbacksTF']);
370827
370891
  }
370828
- await resource.run();
370892
+ await resource.run({ planOnly: opts.planOnly });
370829
370893
  return resource;
370830
370894
  }
370831
370895
  function createInstanceOf(entity, op, deps) {
@@ -371107,6 +371171,56 @@ function helperCreateCheckRunName(cmd, item) {
371107
371171
  return `${item.kind} - ${cmd}`;
371108
371172
  }
371109
371173
 
371174
+ ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
371175
+ const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
371176
+ function replaceConfigSecrets(config, secrets) {
371177
+ for (const key in config) {
371178
+ if (typeof config[key] === 'object' && config[key] !== null) {
371179
+ // If the property is an object, call this function recursively
371180
+ replaceConfigSecrets(config[key], secrets);
371181
+ }
371182
+ else if (typeof config[key] === 'string') {
371183
+ // If the property is a string and its value is equal to secrets.something,
371184
+ // replace the value with the value of the 'something' key in the secrets object
371185
+ config[key] = config[key].replace(secretRegex, (_, group1) => {
371186
+ if (!secrets[group1]) {
371187
+ throw new Error(`Secret ${group1} not found in secrets`);
371188
+ }
371189
+ return secrets[group1];
371190
+ });
371191
+ }
371192
+ }
371193
+ return config;
371194
+ }
371195
+ function replaceInlineSecrets(inline, secrets) {
371196
+ if (typeof inline !== 'string' || !inline)
371197
+ return inline;
371198
+ let result = inline;
371199
+ result = result.replace(secretRegex, (_, group1) => {
371200
+ if (!secrets[group1]) {
371201
+ throw new Error(`Secret ${group1} not found in secrets`);
371202
+ }
371203
+ return secrets[group1];
371204
+ });
371205
+ return result;
371206
+ }
371207
+ /**
371208
+ * Retrieves a policy annotation value from a custom resource
371209
+ * @param item - The CR to get the policy from
371210
+ * @param annotation - The annotation key to retrieve
371211
+ * @returns The policy value, or undefined if not set
371212
+ */
371213
+ function getPolicy(item, annotation) {
371214
+ const policy = item.metadata.annotations && item.metadata.annotations[annotation];
371215
+ if (policy)
371216
+ return policy;
371217
+ }
371218
+
371219
+ ;// CONCATENATED MODULE: ../operator/src/utils/operationErrorMessages.ts
371220
+ const APPLY_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraform apply operation.';
371221
+ const DESTROY_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraform destroy operation.';
371222
+ const PLAN_DEFAULT_ERROR_MESSAGE = 'An error occurred while executing the Terraform plan operation.';
371223
+
371110
371224
  ;// CONCATENATED MODULE: ../operator/cdktf.ts
371111
371225
 
371112
371226
 
@@ -371116,9 +371230,18 @@ function helperCreateCheckRunName(cmd, item) {
371116
371230
 
371117
371231
 
371118
371232
 
371233
+
371234
+ const cdktf_LAST_STATE_PR_ANNOTATION = 'firestartr.dev/last-state-pr';
371235
+
371119
371236
  function processOperation(item, op, handler) {
371120
371237
  operator_src_logger.info(`Processing operation ${op} on ${item.kind}/${item.metadata?.name}`);
371121
371238
  try {
371239
+ const policy = getPolicy(item, 'firestartr.dev/policy');
371240
+ // If general policy is observe/observe-only, route to observe mode instead of apply
371241
+ if (!policy || policy === 'observe' || policy === 'observe-only') {
371242
+ operator_src_logger.info(`Policy is '${policy || 'not set (default)'}', routing to observe mode`);
371243
+ return cdktf_observe(item, op, handler);
371244
+ }
371122
371245
  switch (op) {
371123
371246
  case OperationType.UPDATED:
371124
371247
  return updated(item, op, handler);
@@ -371143,6 +371266,11 @@ function processOperation(item, op, handler) {
371143
371266
  throw e;
371144
371267
  }
371145
371268
  }
371269
+ async function* cdktf_observe(item, op, handler) {
371270
+ for await (const transition of doPlan(item, op, handler)) {
371271
+ yield transition;
371272
+ }
371273
+ }
371146
371274
  async function* created(item, op, handler) {
371147
371275
  for await (const transition of doApply(item, op, handler)) {
371148
371276
  yield transition;
@@ -371178,8 +371306,18 @@ async function* sync(item, op, handler) {
371178
371306
  status: 'False',
371179
371307
  message: 'Synth CDKTF',
371180
371308
  };
371181
- for await (const transition of doApply(item, op, handler)) {
371182
- yield transition;
371309
+ const syncPolicy = getPolicy(item, 'firestartr.dev/sync-policy');
371310
+ if (syncPolicy === 'apply') {
371311
+ operator_src_logger.info(`SYNC OPERATION: applying item ${item.metadata.name} with sync-policy=${syncPolicy}`);
371312
+ for await (const transition of doApply(item, op, handler)) {
371313
+ yield transition;
371314
+ }
371315
+ }
371316
+ else {
371317
+ operator_src_logger.info(`SYNC OPERATION: planning item ${item.metadata.name} with sync-policy=${syncPolicy || 'default (observe)'}`);
371318
+ for await (const transition of doPlan(item, op, handler)) {
371319
+ yield transition;
371320
+ }
371183
371321
  }
371184
371322
  yield {
371185
371323
  item,
@@ -371218,15 +371356,14 @@ async function* markedToDeletion(item, op, handler) {
371218
371356
  message: 'Destroying process started',
371219
371357
  };
371220
371358
  const deps = await handler.resolveReferences();
371221
- const annotation = 'firestartr.dev/last-state-pr';
371222
- const statePr = item?.metadata?.annotations?.[annotation];
371359
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371223
371360
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371224
371361
  if (!hasStatePr) {
371225
371362
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371226
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371363
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371227
371364
  }
371228
371365
  else {
371229
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371366
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371230
371367
  }
371231
371368
  const destroyOutput = await provisioner.runProvisioner({
371232
371369
  mainCr: item,
@@ -371261,7 +371398,7 @@ async function* markedToDeletion(item, op, handler) {
371261
371398
  };
371262
371399
  await handler.finalize(handler.pluralKind, item.metadata.namespace, item, 'firestartr.dev/finalizer');
371263
371400
  await handler.writeTerraformOutputInTfResult(item, output);
371264
- if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
371401
+ if (item.metadata.annotations[cdktf_LAST_STATE_PR_ANNOTATION] || false) {
371265
371402
  await addDestroyCommitStatus(item, 'success', 'Destroy operation completed', `Terraform Destroy ${item.metadata.name}`);
371266
371403
  }
371267
371404
  void handler.success();
@@ -371272,7 +371409,7 @@ async function* markedToDeletion(item, op, handler) {
371272
371409
  reason: op,
371273
371410
  type: 'ERROR',
371274
371411
  status: 'True',
371275
- message: e.toString(),
371412
+ message: DESTROY_DEFAULT_ERROR_MESSAGE,
371276
371413
  };
371277
371414
  // if there is a current checkRun working
371278
371415
  // we close it with an error
@@ -371342,15 +371479,14 @@ async function* doApply(item, op, handler) {
371342
371479
  }
371343
371480
  const deps = await handler.resolveReferences();
371344
371481
  operator_src_logger.info(`Item ${item.metadata.name} has the following dependencies: ${deps}`);
371345
- const annotation = 'firestartr.dev/last-state-pr';
371346
- const statePr = item?.metadata?.annotations?.[annotation];
371482
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371347
371483
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371348
371484
  if (!hasStatePr) {
371349
371485
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371350
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371486
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371351
371487
  }
371352
371488
  else {
371353
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371489
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371354
371490
  }
371355
371491
  const applyOutput = await provisioner.runProvisioner({
371356
371492
  mainCr: item,
@@ -371424,21 +371560,21 @@ async function* doApply(item, op, handler) {
371424
371560
  reason: op,
371425
371561
  type: 'ERROR',
371426
371562
  status: 'True',
371427
- message: error.toString(),
371563
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
371428
371564
  };
371429
371565
  yield {
371430
371566
  item,
371431
371567
  reason: op,
371432
371568
  type: 'PROVISIONED',
371433
371569
  status: 'False',
371434
- message: error.toString(),
371570
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
371435
371571
  };
371436
371572
  yield {
371437
371573
  item,
371438
371574
  reason: op,
371439
371575
  type: 'PROVISIONING',
371440
371576
  status: 'False',
371441
- message: error.toString(),
371577
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
371442
371578
  };
371443
371579
  handler.error();
371444
371580
  if (error) {
@@ -371446,6 +371582,219 @@ async function* doApply(item, op, handler) {
371446
371582
  }
371447
371583
  }
371448
371584
  }
371585
+ async function* doPlan(item, op, handler) {
371586
+ let checkRunCtl;
371587
+ try {
371588
+ cleanTerraformState();
371589
+ yield {
371590
+ item,
371591
+ reason: op,
371592
+ type: 'PLANNING',
371593
+ status: 'True',
371594
+ message: 'Planning process started',
371595
+ };
371596
+ const deps = await handler.resolveReferences();
371597
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371598
+ const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371599
+ if (!hasStatePr) {
371600
+ operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371601
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs for plan.`);
371602
+ }
371603
+ else {
371604
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371605
+ await addPlanStatusCheck(statePr, 'CDKTF plan in progress...');
371606
+ }
371607
+ // Run provisioner in plan-only mode
371608
+ const planResult = await provisioner.runProvisioner({ mainCr: item, deps }, {
371609
+ planOnly: true,
371610
+ delete: 'deletionTimestamp' in item.metadata,
371611
+ ...(hasStatePr
371612
+ ? {
371613
+ logStreamCallbacksCDKTF: {
371614
+ prepare: async () => {
371615
+ checkRunCtl = await GHCheckRun('synth', item);
371616
+ return checkRunCtl;
371617
+ },
371618
+ },
371619
+ logStreamCallbacksTF: {
371620
+ prepare: async () => {
371621
+ checkRunCtl = await GHCheckRun('plan', item);
371622
+ return checkRunCtl;
371623
+ },
371624
+ },
371625
+ }
371626
+ : {}),
371627
+ });
371628
+ const planOutput = planResult?.output || '';
371629
+ // Parse terraform plan output to detect changes
371630
+ // Handles multiple Terraform output formats and versions
371631
+ const hasChanges = detectPlanChanges(planOutput);
371632
+ if (hasChanges) {
371633
+ yield {
371634
+ item,
371635
+ reason: op,
371636
+ type: 'OUT_OF_SYNC',
371637
+ status: 'True',
371638
+ message: 'Plan has changes',
371639
+ };
371640
+ yield {
371641
+ item,
371642
+ reason: op,
371643
+ type: 'PROVISIONED',
371644
+ status: 'False',
371645
+ message: 'Plan has changes',
371646
+ };
371647
+ }
371648
+ else {
371649
+ yield {
371650
+ item,
371651
+ reason: op,
371652
+ type: 'OUT_OF_SYNC',
371653
+ status: 'False',
371654
+ message: 'Plan has no changes',
371655
+ };
371656
+ yield {
371657
+ item,
371658
+ reason: op,
371659
+ type: 'PROVISIONED',
371660
+ status: 'True',
371661
+ message: 'Plan has no changes',
371662
+ };
371663
+ }
371664
+ // Store plan details for later reference
371665
+ yield {
371666
+ item,
371667
+ reason: op,
371668
+ type: 'LAST_PLAN_DETAILS',
371669
+ status: 'Unknown',
371670
+ message: planOutput,
371671
+ };
371672
+ yield {
371673
+ item,
371674
+ reason: op,
371675
+ type: 'PLANNING',
371676
+ status: 'False',
371677
+ message: 'Planning process finished',
371678
+ };
371679
+ if (hasStatePr) {
371680
+ await addPlanStatusCheck(statePr, hasChanges ? 'Plan has changes' : 'Plan has no changes', 'completed');
371681
+ }
371682
+ }
371683
+ catch (e) {
371684
+ operator_src_logger.error(`CDKTF plan failed: ${e}`);
371685
+ if (checkRunCtl) {
371686
+ checkRunCtl.fnOnError(e);
371687
+ }
371688
+ yield {
371689
+ item,
371690
+ reason: op,
371691
+ type: 'ERROR',
371692
+ status: 'True',
371693
+ message: e.toString(),
371694
+ };
371695
+ yield {
371696
+ item,
371697
+ reason: op,
371698
+ type: 'PLANNING',
371699
+ status: 'False',
371700
+ message: e.toString(),
371701
+ };
371702
+ yield {
371703
+ item,
371704
+ reason: op,
371705
+ type: 'PROVISIONED',
371706
+ status: 'False',
371707
+ message: e.toString(),
371708
+ };
371709
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371710
+ if (statePr) {
371711
+ const summaryText = tryCreateErrorSummary('CDKTF Plan failed', e);
371712
+ await addPlanStatusCheck(statePr, summaryText, 'completed', true);
371713
+ }
371714
+ await handler.writeTerraformOutputInTfResult(item, e);
371715
+ void handler.error();
371716
+ }
371717
+ }
371718
+ /**
371719
+ * Detects if a Terraform plan output contains changes
371720
+ * Handles multiple Terraform versions and output formats
371721
+ * @param planOutput - The text output from terraform plan
371722
+ * @returns true if changes are detected, false otherwise
371723
+ */
371724
+ function detectPlanChanges(planOutput) {
371725
+ if (!planOutput || planOutput.trim().length === 0) {
371726
+ return false;
371727
+ }
371728
+ // Normalize the output for consistent matching
371729
+ const normalized = planOutput.toLowerCase();
371730
+ // Pattern 1: "Plan: X to add, Y to change, Z to destroy"
371731
+ // Matches: "Plan: 1 to add, 0 to change, 0 to destroy"
371732
+ const planPattern = /plan:\s*(\d+)\s+to\s+add,\s*(\d+)\s+to\s+change,\s*(\d+)\s+to\s+destroy/i;
371733
+ const planMatch = planOutput.match(planPattern);
371734
+ if (planMatch) {
371735
+ const add = Number(planMatch[1]);
371736
+ const change = Number(planMatch[2]);
371737
+ const destroy = Number(planMatch[3]);
371738
+ if (add > 0 || change > 0 || destroy > 0) {
371739
+ return true;
371740
+ }
371741
+ // Explicitly found "Plan:" with 0/0/0 - no changes
371742
+ return false;
371743
+ }
371744
+ // Pattern 2: Individual change indicators
371745
+ // Handles variations like "1 to add", "2 to change", "3 to destroy"
371746
+ const hasAdditions = /\d+\s+to\s+add/i.test(planOutput);
371747
+ const hasChanges = /\d+\s+to\s+change/i.test(planOutput);
371748
+ const hasDestructions = /\d+\s+to\s+destroy/i.test(planOutput);
371749
+ const hasImports = /\d+\s+to\s+import/i.test(planOutput);
371750
+ if (hasAdditions || hasChanges || hasDestructions || hasImports) {
371751
+ return true;
371752
+ }
371753
+ // Pattern 3: Resource-level change indicators
371754
+ // Matches: "# resource will be created", "# resource will be updated", etc.
371755
+ const resourceChangePatterns = [
371756
+ /will\s+be\s+(created|destroyed|updated|replaced)/i,
371757
+ /must\s+be\s+(created|destroyed|updated|replaced)/i,
371758
+ /#.*\s+(create|destroy|update|replace)/i,
371759
+ ];
371760
+ for (const pattern of resourceChangePatterns) {
371761
+ if (pattern.test(planOutput)) {
371762
+ return true;
371763
+ }
371764
+ }
371765
+ // Pattern 4: Action symbols in plan output
371766
+ // Terraform uses symbols like +, -, ~, -/+ to indicate changes
371767
+ const actionSymbols = [
371768
+ /^\s*[+]\s+/m,
371769
+ /^\s*[-]\s+/m,
371770
+ /^\s*[~]\s+/m,
371771
+ /^\s*[-][/][+]\s+/m, // Replace
371772
+ ];
371773
+ for (const pattern of actionSymbols) {
371774
+ if (pattern.test(planOutput)) {
371775
+ return true;
371776
+ }
371777
+ }
371778
+ // Pattern 5: No changes messages (inverse check)
371779
+ const noChangesPatterns = [
371780
+ /no\s+changes/i,
371781
+ /infrastructure\s+is\s+up[-\s]to[-\s]date/i,
371782
+ /your\s+infrastructure\s+matches\s+the\s+configuration/i,
371783
+ /0\s+to\s+add,\s*0\s+to\s+change,\s*0\s+to\s+destroy/i,
371784
+ ];
371785
+ for (const pattern of noChangesPatterns) {
371786
+ if (pattern.test(planOutput)) {
371787
+ return false;
371788
+ }
371789
+ }
371790
+ // If we find "Plan:" keyword but couldn't parse it, log a warning and assume no changes
371791
+ if (normalized.includes('plan:')) {
371792
+ operator_src_logger.warn('Found "Plan:" in output but could not parse change counts. Assuming no changes.');
371793
+ return false;
371794
+ }
371795
+ // Default: assume no changes if we can't detect any
371796
+ return false;
371797
+ }
371449
371798
  function cleanTerraformState() {
371450
371799
  external_fs_.rmSync('/library/packages/provisioner/cdktf.out', {
371451
371800
  recursive: true,
@@ -372445,40 +372794,6 @@ function tf_checkrun_helperCreateCheckRunName(cmd) {
372445
372794
  return `TFWorkspace - ${cmd}`;
372446
372795
  }
372447
372796
 
372448
- ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
372449
- const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
372450
- function replaceConfigSecrets(config, secrets) {
372451
- for (const key in config) {
372452
- if (typeof config[key] === 'object' && config[key] !== null) {
372453
- // If the property is an object, call this function recursively
372454
- replaceConfigSecrets(config[key], secrets);
372455
- }
372456
- else if (typeof config[key] === 'string') {
372457
- // If the property is a string and its value is equal to secrets.something,
372458
- // replace the value with the value of the 'something' key in the secrets object
372459
- config[key] = config[key].replace(secretRegex, (_, group1) => {
372460
- if (!secrets[group1]) {
372461
- throw new Error(`Secret ${group1} not found in secrets`);
372462
- }
372463
- return secrets[group1];
372464
- });
372465
- }
372466
- }
372467
- return config;
372468
- }
372469
- function replaceInlineSecrets(inline, secrets) {
372470
- if (typeof inline !== 'string' || !inline)
372471
- return inline;
372472
- let result = inline;
372473
- result = result.replace(secretRegex, (_, group1) => {
372474
- if (!secrets[group1]) {
372475
- throw new Error(`Secret ${group1} not found in secrets`);
372476
- }
372477
- return secrets[group1];
372478
- });
372479
- return result;
372480
- }
372481
-
372482
372797
  ;// CONCATENATED MODULE: ../operator/src/tfworkspaces/process-operation.ts
372483
372798
 
372484
372799
 
@@ -372491,6 +372806,7 @@ function replaceInlineSecrets(inline, secrets) {
372491
372806
 
372492
372807
 
372493
372808
 
372809
+
372494
372810
  const TF_PROJECTS_PATH = '/tmp/tfworkspaces';
372495
372811
  function process_operation_processOperation(item, op, handler) {
372496
372812
  try {
@@ -372626,28 +372942,28 @@ async function* doPlanJSONFormat(item, op, handler) {
372626
372942
  reason: op,
372627
372943
  type: 'PROVISIONED',
372628
372944
  status: 'False',
372629
- message: JSON.stringify(e),
372945
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
372630
372946
  };
372631
372947
  yield {
372632
372948
  item,
372633
372949
  reason: op,
372634
372950
  type: 'PLANNING',
372635
372951
  status: 'False',
372636
- message: JSON.stringify(e),
372952
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
372637
372953
  };
372638
372954
  yield {
372639
372955
  item,
372640
372956
  reason: op,
372641
372957
  type: 'OUT_OF_SYNC',
372642
372958
  status: 'False',
372643
- message: 'Error observing item',
372959
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
372644
372960
  };
372645
372961
  yield {
372646
372962
  item,
372647
372963
  reason: op,
372648
372964
  type: 'ERROR',
372649
372965
  status: 'True',
372650
- message: JSON.stringify(e),
372966
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
372651
372967
  };
372652
372968
  const summaryText = tryCreateErrorSummary('Terraform Plan failed', e);
372653
372969
  if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
@@ -372669,11 +372985,6 @@ async function* process_operation_renamed(item, op, handler) {
372669
372985
  yield transition;
372670
372986
  }
372671
372987
  }
372672
- function getPolicy(item, annotation) {
372673
- const policy = item.metadata.annotations && item.metadata.annotations[annotation];
372674
- if (policy)
372675
- return policy;
372676
- }
372677
372988
  async function* process_operation_updated(item, op, handler) {
372678
372989
  for await (const transition of process_operation_doApply(item, op, handler)) {
372679
372990
  yield transition;
@@ -372818,7 +373129,7 @@ async function* process_operation_markedToDeletion(item, op, handler) {
372818
373129
  reason: op,
372819
373130
  type: 'ERROR',
372820
373131
  status: 'True',
372821
- message: e.toString(),
373132
+ message: DESTROY_DEFAULT_ERROR_MESSAGE,
372822
373133
  };
372823
373134
  await handler.writeTerraformOutputInTfResult(item, e);
372824
373135
  if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
@@ -372936,21 +373247,21 @@ async function* process_operation_doApply(item, op, handler) {
372936
373247
  reason: op,
372937
373248
  type: 'ERROR',
372938
373249
  status: 'True',
372939
- message: JSON.stringify(e),
373250
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
372940
373251
  };
372941
373252
  yield {
372942
373253
  item,
372943
373254
  reason: op,
372944
373255
  type: 'PROVISIONED',
372945
373256
  status: 'False',
372946
- message: JSON.stringify(e),
373257
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
372947
373258
  };
372948
373259
  yield {
372949
373260
  item,
372950
373261
  reason: op,
372951
373262
  type: 'PROVISIONING',
372952
373263
  status: 'False',
372953
- message: JSON.stringify(e),
373264
+ message: APPLY_DEFAULT_ERROR_MESSAGE,
372954
373265
  };
372955
373266
  handler.error();
372956
373267
  if (e) {
@@ -373313,11 +373624,12 @@ async function acquireLease(namespace, cb, interval = 10000) {
373313
373624
 
373314
373625
 
373315
373626
 
373627
+
373316
373628
  const processOperationPlan_TF_PROJECTS_PATH = '/tmp/tfworkspaces';
373317
373629
  function processOperationPlan(item, op, handler) {
373318
373630
  try {
373319
373631
  processOperationPlan_clearLocalTfProjects();
373320
- const policy = processOperationPlan_getPolicy(item);
373632
+ const policy = getPolicy(item, 'firestartr.dev/policy');
373321
373633
  if (policy === 'observe' || policy === 'apply') {
373322
373634
  return processOperationPlan_plan(item, op, handler);
373323
373635
  }
@@ -373426,21 +373738,21 @@ async function* doPlanPlainTextFormat(item, op, handler, action) {
373426
373738
  reason: op,
373427
373739
  type: 'PROVISIONED',
373428
373740
  status: 'False',
373429
- message: JSON.stringify(e),
373741
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373430
373742
  };
373431
373743
  yield {
373432
373744
  item,
373433
373745
  reason: op,
373434
373746
  type: 'PLANNING',
373435
373747
  status: 'False',
373436
- message: JSON.stringify(e),
373748
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373437
373749
  };
373438
373750
  yield {
373439
373751
  item,
373440
373752
  reason: op,
373441
373753
  type: 'ERROR',
373442
373754
  status: 'True',
373443
- message: JSON.stringify(e),
373755
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373444
373756
  };
373445
373757
  const summaryText = tryCreateErrorSummary('Terraform Plan failed', e);
373446
373758
  if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
@@ -373544,28 +373856,28 @@ async function* processOperationPlan_doPlanJSONFormat(item, op, handler, action)
373544
373856
  reason: op,
373545
373857
  type: 'PROVISIONED',
373546
373858
  status: 'False',
373547
- message: JSON.stringify(e),
373859
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373548
373860
  };
373549
373861
  yield {
373550
373862
  item,
373551
373863
  reason: op,
373552
373864
  type: 'PLANNING',
373553
373865
  status: 'False',
373554
- message: JSON.stringify(e),
373866
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373555
373867
  };
373556
373868
  yield {
373557
373869
  item,
373558
373870
  reason: op,
373559
373871
  type: 'OUT_OF_SYNC',
373560
373872
  status: 'False',
373561
- message: 'Error observing item',
373873
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373562
373874
  };
373563
373875
  yield {
373564
373876
  item,
373565
373877
  reason: op,
373566
373878
  type: 'ERROR',
373567
373879
  status: 'True',
373568
- message: JSON.stringify(e),
373880
+ message: PLAN_DEFAULT_ERROR_MESSAGE,
373569
373881
  };
373570
373882
  void handler.error();
373571
373883
  if (e) {
@@ -373764,12 +374076,6 @@ function processOperationPlan_getErrorOutputMessage(cr, key, ref) {
373764
374076
  throw new Error(`❌ Source ${cr.spec.source} not supported`);
373765
374077
  }
373766
374078
  }
373767
- function processOperationPlan_getPolicy(item) {
373768
- const policy = item.metadata.annotations &&
373769
- item.metadata.annotations['firestartr.dev/policy'];
373770
- if (policy)
373771
- return policy;
373772
- }
373773
374079
 
373774
374080
  ;// CONCATENATED MODULE: ../operator/src/ctx.ts
373775
374081
  class Ctx {