@firestartr/cli 1.53.0-snapshot-4 → 1.53.0-snapshot-5

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
@@ -354119,7 +354119,7 @@ async function getUserInfo(name) {
354119
354119
  });
354120
354120
 
354121
354121
  ;// CONCATENATED MODULE: ../github/src/sticky_comment.ts
354122
-
354122
+ const MAX_MULTIPART_COMMENTS = 100;
354123
354123
  const locks = new Map();
354124
354124
  async function withLock(key, fn) {
354125
354125
  const prev = locks.get(key) ?? Promise.resolve();
@@ -354140,116 +354140,147 @@ async function withLock(key, fn) {
354140
354140
  function escapeForRegex(s) {
354141
354141
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
354142
354142
  }
354143
- function prIdRegex(kind) {
354144
- return new RegExp(`<!-- sticky-id:${escapeForRegex(kind)}=(\\d+) -->`);
354143
+ function multiPartIdRegex(baseKind, index) {
354144
+ return new RegExp(`<!-- sticky-id:${escapeForRegex(baseKind)}\\[${index}\\]=(\\d+) -->`);
354145
354145
  }
354146
- function prIdMarker(kind, id) {
354147
- return `<!-- sticky-id:${kind}=${id} -->`;
354146
+ function multiPartIdMarker(baseKind, index, id) {
354147
+ return `<!-- sticky-id:${baseKind}[${index}]=${id} -->`;
354148
354148
  }
354149
- function bodyMarker(kind) {
354150
- return `<!-- sticky:kind=${kind} -->`;
354149
+ function multiPartBodyMarker(baseKind, index) {
354150
+ return `<!-- sticky:kind=${baseKind}[${index}] -->`;
354151
354151
  }
354152
- async function readStickyIdFromPrBody(octokit, owner, repo, pr, kind) {
354152
+ // ---- Helpers for reducing API calls and duplication ----
354153
+ async function getPrBody(octokit, owner, repo, pr) {
354153
354154
  const prResp = await octokit.rest.pulls.get({ owner, repo, pull_number: pr });
354154
- const body = prResp.data?.body ?? '';
354155
- const m = body.match(prIdRegex(kind));
354156
- github_src_logger.info(`readStickyIdFromPrBody: owner=${owner} repo=${repo} pr=${pr} kind=${kind} => id=${m ? m[1] : 'undefined'}`);
354157
- return m ? Number(m[1]) : undefined;
354158
- }
354159
- async function writeStickyIdToPrBody(octokit, owner, repo, pr, kind, id) {
354160
- const get = await octokit.rest.pulls.get({ owner, repo, pull_number: pr });
354161
- const body = get.data?.body ?? '';
354162
- const rx = prIdRegex(kind);
354163
- const marker = prIdMarker(kind, id);
354164
- github_src_logger.info(`writeStickyIdToPrBody: owner=${owner} repo=${repo} pr=${pr} kind=${kind} id=${id}`);
354165
- const next = rx.test(body)
354166
- ? body.replace(rx, marker)
354167
- : body
354168
- ? `${body}\n${marker}`
354169
- : marker;
354170
- if (next !== body) {
354155
+ return prResp.data?.body ?? '';
354156
+ }
354157
+ async function updatePrBodyIfChanged(octokit, owner, repo, pr, newBody, oldBody) {
354158
+ if (newBody !== oldBody) {
354171
354159
  await octokit.rest.pulls.update({
354172
354160
  owner,
354173
354161
  repo,
354174
354162
  pull_number: pr,
354175
- body: next,
354163
+ body: newBody,
354176
354164
  });
354177
354165
  }
354178
354166
  }
354179
- async function upsertStickyComment(octokit, params) {
354180
- const { owner, repo, pullNumber, kind } = params;
354181
- const fullBody = `${params.body}\n\n${bodyMarker(kind)}`;
354182
- const lockKey = `${owner}/${repo}#${pullNumber}#${kind}`;
354183
- await withLock(lockKey, async () => {
354184
- // 1) PR-body registry fast path
354185
- const idFromPr = await readStickyIdFromPrBody(octokit, owner, repo, pullNumber, kind);
354186
- if (idFromPr) {
354187
- github_src_logger.info(`upsertStickyComment: found existing comment ID ${idFromPr} from PR body for kind=${kind}`);
354188
- await octokit.rest.issues.updateComment({
354189
- owner,
354190
- repo,
354191
- comment_id: idFromPr,
354192
- body: fullBody,
354193
- });
354194
- return;
354167
+ async function updateComment(octokit, owner, repo, commentId, body) {
354168
+ await octokit.rest.issues.updateComment({
354169
+ owner,
354170
+ repo,
354171
+ comment_id: commentId,
354172
+ body,
354173
+ });
354174
+ }
354175
+ async function createCommentAndGetId(octokit, owner, repo, pullNumber, body) {
354176
+ const created = await octokit.rest.issues.createComment({
354177
+ owner,
354178
+ repo,
354179
+ issue_number: pullNumber,
354180
+ body,
354181
+ });
354182
+ return created?.data?.id;
354183
+ }
354184
+ async function readMultiPartStickyIdsFromPrBody(octokit, owner, repo, pr, baseKind) {
354185
+ const body = await getPrBody(octokit, owner, repo, pr);
354186
+ const ids = new Map();
354187
+ // Find all indexed markers for this base kind
354188
+ // Break early once we encounter a gap (most comments are sequentially indexed)
354189
+ let consecutiveGaps = 0;
354190
+ for (let i = 0; i < MAX_MULTIPART_COMMENTS; i++) {
354191
+ const m = body.match(multiPartIdRegex(baseKind, i));
354192
+ if (m) {
354193
+ ids.set(i, Number(m[1]));
354194
+ consecutiveGaps = 0;
354195
+ }
354196
+ else {
354197
+ consecutiveGaps++;
354198
+ // Stop after finding 5 consecutive gaps (reasonable buffer for sparse indices)
354199
+ if (consecutiveGaps >= 5)
354200
+ break;
354195
354201
  }
354196
- // 2) Try to find existing comments by kind marker
354197
- try {
354198
- const all = await octokit.paginate(octokit.rest.issues.listComments, {
354199
- owner,
354200
- repo,
354201
- issue_number: pullNumber,
354202
- per_page: 100,
354203
- });
354204
- const marker = bodyMarker(kind);
354205
- const matches = all.filter((c) => typeof c.body === 'string' && c.body.includes(marker));
354206
- if (matches.length > 0) {
354207
- // Sort by created_at to identify the oldest comment
354208
- const sorted = matches.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
354209
- const primaryComment = sorted[0];
354210
- // Update the primary (oldest) comment
354211
- github_src_logger.info(`upsertStickyComment: found existing comment ID ${primaryComment.id} by marker for kind=${kind}, updating it and deleting ${sorted.length - 1} duplicates`);
354212
- await octokit.rest.issues.updateComment({
354213
- owner,
354214
- repo,
354215
- comment_id: primaryComment.id,
354216
- body: fullBody,
354217
- });
354218
- // Store its ID in PR body
354219
- await writeStickyIdToPrBody(octokit, owner, repo, pullNumber, kind, primaryComment.id);
354220
- // Delete all duplicate comments (from index 1 onwards)
354221
- for (let i = 1; i < sorted.length; i++) {
354222
- github_src_logger.info(`upsertStickyComment: deleting duplicate comment ID ${sorted[i].id} for kind=${kind}`);
354223
- try {
354224
- await octokit.rest.issues.deleteComment({
354225
- owner,
354226
- repo,
354227
- comment_id: sorted[i].id,
354228
- });
354229
- }
354230
- catch (deleteErr) {
354231
- // Log but continue deleting others
354232
- github_src_logger.warn(`Failed to delete duplicate comment ${sorted[i].id} for ${kind}: ${deleteErr}`);
354233
- }
354202
+ }
354203
+ return ids;
354204
+ }
354205
+ async function writeMultiPartStickyIdsToPrBody(octokit, owner, repo, pr, baseKind, ids) {
354206
+ const oldBody = await getPrBody(octokit, owner, repo, pr);
354207
+ let body = oldBody;
354208
+ // Determine the highest index we need to clean up
354209
+ // Add a small buffer to catch any stragglers from previous runs
354210
+ const maxIndex = ids.size > 0 ? Math.max(...ids.keys()) + 5 : 5;
354211
+ // Remove all old markers for this base kind (only up to needed range)
354212
+ for (let i = 0; i <= maxIndex && i < MAX_MULTIPART_COMMENTS; i++) {
354213
+ const rx = multiPartIdRegex(baseKind, i);
354214
+ body = body.replace(rx, '');
354215
+ }
354216
+ // Add all new markers
354217
+ let markers = '';
354218
+ ids.forEach((id, index) => {
354219
+ markers += `${multiPartIdMarker(baseKind, index, id)}\n`;
354220
+ });
354221
+ // Clean up extra newlines
354222
+ markers = markers.trim();
354223
+ let next = body.trim();
354224
+ if (markers) {
354225
+ next = next ? `${next}\n${markers}` : markers;
354226
+ }
354227
+ await updatePrBodyIfChanged(octokit, owner, repo, pr, next, oldBody);
354228
+ }
354229
+ /**
354230
+ * Upsert sticky comments for one or more parts.
354231
+ * Always uses indexed kinds (e.g., 'logs[0]', 'logs[1]', etc.) for consistency.
354232
+ *
354233
+ * Behavior:
354234
+ * - Single part (bodies.length === 1): Uses indexed kind like 'logs[0]'
354235
+ * - Multiple parts (bodies.length > 1): Uses indexed kinds 'logs[0]', 'logs[1]', etc.
354236
+ * - Non-last parts include a note about continuation
354237
+ * - Unused parts (from previous runs) are updated with a placeholder message
354238
+ * - Never deletes comments
354239
+ */
354240
+ async function upsertMultiPartStickyComments(octokit, params) {
354241
+ const { owner, repo, pullNumber, baseKind, bodies } = params;
354242
+ // Always use indexed kinds (e.g., 'logs[0]', 'logs[1]', etc.)
354243
+ const lockKey = `${owner}/${repo}#${pullNumber}#${baseKind}[multi]`;
354244
+ const totalParts = bodies.length;
354245
+ await withLock(lockKey, async () => {
354246
+ // Read existing comment IDs for this base kind
354247
+ const existingIds = await readMultiPartStickyIdsFromPrBody(octokit, owner, repo, pullNumber, baseKind);
354248
+ const newIds = new Map();
354249
+ // Process each part of the new content
354250
+ for (let partIndex = 0; partIndex < totalParts; partIndex++) {
354251
+ const body = bodies[partIndex];
354252
+ const isLastPart = partIndex === totalParts - 1;
354253
+ // Build the full comment body with continuation notice for non-last parts
354254
+ let fullBody = body;
354255
+ if (!isLastPart) {
354256
+ fullBody += `\n\n📝 **Note:** This is part ${partIndex + 1} of ${totalParts}. See next comment for continuation.`;
354257
+ }
354258
+ fullBody += `\n\n${multiPartBodyMarker(baseKind, partIndex)}`;
354259
+ const existingId = existingIds.get(partIndex);
354260
+ if (existingId) {
354261
+ // Update existing comment
354262
+ await updateComment(octokit, owner, repo, existingId, fullBody);
354263
+ newIds.set(partIndex, existingId);
354264
+ }
354265
+ else {
354266
+ // Create new comment
354267
+ const newId = await createCommentAndGetId(octokit, owner, repo, pullNumber, fullBody);
354268
+ if (newId !== undefined) {
354269
+ newIds.set(partIndex, newId);
354234
354270
  }
354235
- return;
354236
354271
  }
354237
354272
  }
354238
- catch (err) {
354239
- // if paginate/listComments not available, we'll fall back to creating
354240
- github_src_logger.warn(`Failed to list comments for kind=${kind} on PR #${pullNumber}: ${err}`);
354241
- }
354242
- // 3) Create new comment and register
354243
- const created = await octokit.rest.issues.createComment({
354244
- owner,
354245
- repo,
354246
- issue_number: pullNumber,
354247
- body: fullBody,
354248
- });
354249
- const newId = created?.data?.id;
354250
- if (newId !== undefined) {
354251
- await writeStickyIdToPrBody(octokit, owner, repo, pullNumber, kind, newId);
354273
+ // Handle old comments that are no longer needed (when new content is shorter)
354274
+ // Update them with a placeholder message instead of deleting, but keep their IDs for potential reuse
354275
+ for (const [oldIndex, oldId] of existingIds) {
354276
+ if (!newIds.has(oldIndex)) {
354277
+ const placeholderBody = `⚠️ This comment is no longer in use as the log output has been reduced.\n\n${multiPartBodyMarker(baseKind, oldIndex)}`;
354278
+ await updateComment(octokit, owner, repo, oldId, placeholderBody);
354279
+ newIds.set(oldIndex, oldId); // Preserve ID in PR body for reuse
354280
+ }
354252
354281
  }
354282
+ // Update PR body with all comment IDs
354283
+ await writeMultiPartStickyIdsToPrBody(octokit, owner, repo, pullNumber, baseKind, newIds);
354253
354284
  });
354254
354285
  }
354255
354286
 
@@ -354263,12 +354294,12 @@ async function commentInPR(comment, pr_number, repo, owner = 'prefapp', stickyKi
354263
354294
  github_src_logger.info(`Commenting ${comment} in PR ${pr_number} of ${owner}/${repo}`);
354264
354295
  const octokit = await getOctokitForOrg(owner);
354265
354296
  if (stickyKind) {
354266
- await upsertStickyComment(octokit, {
354297
+ await upsertMultiPartStickyComments(octokit, {
354267
354298
  owner,
354268
354299
  repo,
354269
354300
  pullNumber: pr_number,
354270
- kind: stickyKind,
354271
- body: comment,
354301
+ baseKind: stickyKind,
354302
+ bodies: [comment],
354272
354303
  });
354273
354304
  }
354274
354305
  else {
@@ -354751,12 +354782,12 @@ class GithubCheckRun {
354751
354782
  return;
354752
354783
  const base = this.checkRunComment ?? '';
354753
354784
  const linkLine = base ? `${base}[here](${link})` : `[here](${link})`;
354754
- await upsertStickyComment(this.octokit, {
354785
+ await upsertMultiPartStickyComments(this.octokit, {
354755
354786
  owner: this.owner,
354756
354787
  repo: this.repo,
354757
354788
  pullNumber: this.pullNumber,
354758
- kind: `check-run:${this.name}`,
354759
- body: linkLine,
354789
+ baseKind: `check-run:${this.name}`,
354790
+ bodies: [linkLine],
354760
354791
  });
354761
354792
  }
354762
354793
  }
@@ -354854,6 +354885,7 @@ async function encryptRepoSecret(owner, repo, section, plaintextValue) {
354854
354885
 
354855
354886
 
354856
354887
 
354888
+
354857
354889
  /* harmony default export */ const github_0 = ({
354858
354890
  org: organization,
354859
354891
  repo: repository,
@@ -354870,6 +354902,7 @@ async function encryptRepoSecret(owner, repo, section, plaintextValue) {
354870
354902
  feedback: {
354871
354903
  createCheckRun: createCheckRun,
354872
354904
  CheckRun: CheckRun,
354905
+ upsertMultiPartStickyComments: upsertMultiPartStickyComments,
354873
354906
  },
354874
354907
  encryption: {
354875
354908
  encryptRepoSecret: encryptRepoSecret,
@@ -357539,6 +357572,69 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357539
357572
  },
357540
357573
  additionalProperties: false,
357541
357574
  },
357575
+ PolicyType: {
357576
+ $id: 'firestartr.dev://common/PolicyType',
357577
+ type: 'string',
357578
+ description: 'Policy for resource management',
357579
+ enum: [
357580
+ 'apply',
357581
+ 'create-only',
357582
+ 'create-update-only',
357583
+ 'full-control',
357584
+ 'observe',
357585
+ 'observe-only',
357586
+ ],
357587
+ },
357588
+ SyncConfig: {
357589
+ $id: 'firestartr.dev://common/SyncConfig',
357590
+ type: 'object',
357591
+ description: 'Sync configuration for resources',
357592
+ properties: {
357593
+ enabled: {
357594
+ type: 'boolean',
357595
+ description: 'Enable periodic sync operations',
357596
+ },
357597
+ period: {
357598
+ type: 'string',
357599
+ pattern: '^[0-9]+[smhd]$',
357600
+ description: 'Sync period (e.g., 1h, 30m, 5s). Must be enabled without schedule.',
357601
+ },
357602
+ schedule: {
357603
+ type: 'string',
357604
+ description: 'Cron schedule for sync operations. Must be enabled without period.',
357605
+ },
357606
+ schedule_timezone: {
357607
+ type: 'string',
357608
+ description: 'Timezone for cron schedule (e.g., UTC, America/New_York)',
357609
+ },
357610
+ policy: {
357611
+ type: 'string',
357612
+ description: 'Policy for sync operations (apply or observe)',
357613
+ },
357614
+ },
357615
+ additionalProperties: false,
357616
+ required: ['enabled'],
357617
+ oneOf: [
357618
+ {
357619
+ required: ['period'],
357620
+ },
357621
+ {
357622
+ required: ['schedule'],
357623
+ },
357624
+ {
357625
+ not: {
357626
+ anyOf: [
357627
+ {
357628
+ required: ['period'],
357629
+ },
357630
+ {
357631
+ required: ['schedule'],
357632
+ },
357633
+ ],
357634
+ },
357635
+ },
357636
+ ],
357637
+ },
357542
357638
  },
357543
357639
  });
357544
357640
 
@@ -357899,6 +357995,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357899
357995
  {
357900
357996
  type: 'object',
357901
357997
  properties: {
357998
+ policy: {
357999
+ $ref: 'firestartr.dev://common/PolicyType',
358000
+ },
357902
358001
  privacy: {
357903
358002
  type: 'string',
357904
358003
  enum: ['closed', 'secret'],
@@ -357909,6 +358008,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357909
358008
  org: {
357910
358009
  type: 'string',
357911
358010
  },
358011
+ sync: {
358012
+ $ref: 'firestartr.dev://common/SyncConfig',
358013
+ },
357912
358014
  },
357913
358015
  required: ['org', 'privacy'],
357914
358016
  },
@@ -357934,6 +358036,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357934
358036
  {
357935
358037
  type: 'object',
357936
358038
  properties: {
358039
+ policy: {
358040
+ $ref: 'firestartr.dev://common/PolicyType',
358041
+ },
357937
358042
  role: {
357938
358043
  type: 'string',
357939
358044
  enum: ['admin', 'member'],
@@ -357941,6 +358046,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357941
358046
  org: {
357942
358047
  type: 'string',
357943
358048
  },
358049
+ sync: {
358050
+ $ref: 'firestartr.dev://common/SyncConfig',
358051
+ },
357944
358052
  },
357945
358053
  required: ['org', 'role'],
357946
358054
  },
@@ -357965,6 +358073,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357965
358073
  {
357966
358074
  type: 'object',
357967
358075
  properties: {
358076
+ policy: {
358077
+ $ref: 'firestartr.dev://common/PolicyType',
358078
+ },
357968
358079
  org: {
357969
358080
  type: 'string',
357970
358081
  description: 'The github organization name',
@@ -357973,6 +358084,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
357973
358084
  type: 'string',
357974
358085
  enum: ['private', 'public', 'internal'],
357975
358086
  },
358087
+ sync: {
358088
+ $ref: 'firestartr.dev://common/SyncConfig',
358089
+ },
357976
358090
  features: {
357977
358091
  type: 'array',
357978
358092
  items: {
@@ -358008,6 +358122,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358008
358122
  {
358009
358123
  type: 'object',
358010
358124
  properties: {
358125
+ policy: {
358126
+ $ref: 'firestartr.dev://common/PolicyType',
358127
+ },
358011
358128
  orgName: {
358012
358129
  type: 'string',
358013
358130
  description: 'Organization name on GitHub',
@@ -358041,6 +358158,9 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
358041
358158
  },
358042
358159
  required: ['url', 'contentType', 'events', 'secretRef'],
358043
358160
  },
358161
+ sync: {
358162
+ $ref: 'firestartr.dev://common/SyncConfig',
358163
+ },
358044
358164
  },
358045
358165
  required: ['orgName', 'webhook'],
358046
358166
  },
@@ -358212,15 +358332,7 @@ const GithubSchemas = [
358212
358332
  type: 'object',
358213
358333
  properties: {
358214
358334
  policy: {
358215
- type: 'string',
358216
- enum: [
358217
- 'apply',
358218
- 'create-only',
358219
- 'create-update-only',
358220
- 'full-control',
358221
- 'observe',
358222
- 'observe-only',
358223
- ],
358335
+ $ref: 'firestartr.dev://common/PolicyType',
358224
358336
  },
358225
358337
  name: {
358226
358338
  type: 'string',
@@ -358230,47 +358342,7 @@ const GithubSchemas = [
358230
358342
  enum: ['remote', 'inline', 'Remote', 'Inline'],
358231
358343
  },
358232
358344
  sync: {
358233
- type: 'object',
358234
- properties: {
358235
- enabled: {
358236
- type: 'boolean',
358237
- },
358238
- period: {
358239
- type: 'string',
358240
- pattern: '^[0-9]+[smhd]$',
358241
- },
358242
- schedule: {
358243
- type: 'string',
358244
- },
358245
- schedule_timezone: {
358246
- type: 'string',
358247
- },
358248
- policy: {
358249
- type: 'string',
358250
- },
358251
- },
358252
- additionalProperties: false,
358253
- required: ['enabled'],
358254
- oneOf: [
358255
- {
358256
- required: ['period'],
358257
- },
358258
- {
358259
- required: ['schedule'],
358260
- },
358261
- {
358262
- not: {
358263
- anyOf: [
358264
- {
358265
- required: ['period'],
358266
- },
358267
- {
358268
- required: ['schedule'],
358269
- },
358270
- ],
358271
- },
358272
- },
358273
- ],
358345
+ $ref: 'firestartr.dev://common/SyncConfig',
358274
358346
  },
358275
358347
  valuesSchema: {
358276
358348
  type: 'string',
@@ -370377,10 +370449,15 @@ class Resource {
370377
370449
  this.set('operation', operation);
370378
370450
  this.set('deps', deps);
370379
370451
  }
370380
- async run() {
370452
+ async run(options) {
370381
370453
  await this.preprocess();
370382
370454
  await this.synth();
370383
- await this.runTerraform();
370455
+ if (options?.planOnly) {
370456
+ await this.runTerraformPlanOnly();
370457
+ }
370458
+ else {
370459
+ await this.runTerraform();
370460
+ }
370384
370461
  await this.postprocess();
370385
370462
  if (this.logStream) {
370386
370463
  this.logStream.end();
@@ -370405,6 +370482,13 @@ class Resource {
370405
370482
  log(msg) {
370406
370483
  this.logFn(msg);
370407
370484
  }
370485
+ async runTerraformPlanOnly() {
370486
+ await this.onTFStreaming();
370487
+ let output = '';
370488
+ output += await terraformInit(this.get('main_artifact'), this.logStream);
370489
+ output += await terraformPlan(this.get('main_artifact'), this.logStream);
370490
+ this.set('output', output);
370491
+ }
370408
370492
  async runTerraform() {
370409
370493
  await this.onTFStreaming();
370410
370494
  let output = '';
@@ -370792,7 +370876,7 @@ async function runProvisioner(data, opts) {
370792
370876
  if ('logStreamCallbacksTF' in opts) {
370793
370877
  resource.setTFStreamLogs(opts['logStreamCallbacksTF']);
370794
370878
  }
370795
- await resource.run();
370879
+ await resource.run({ planOnly: opts.planOnly });
370796
370880
  return resource;
370797
370881
  }
370798
370882
  function createInstanceOf(entity, op, deps) {
@@ -370892,25 +370976,33 @@ async function tryPublishDestroy(item, destroyOutput) {
370892
370976
  return;
370893
370977
  }
370894
370978
  const dividedOutput = github_0.pulls.divideCommentIntoChunks(destroyOutput, 250);
370895
- let currentCommentNo = 1;
370896
- for (const commentContent of dividedOutput) {
370897
- const comment = `<h1>
370979
+ const commentBodies = dividedOutput.map((commentContent, index) => {
370980
+ const isMultiPart = dividedOutput.length > 1;
370981
+ const partIndicator = isMultiPart ? ` (Part ${index + 1})` : '';
370982
+ return `<h1>
370898
370983
  <img width="25" src="https://raw.githubusercontent.com/firestartr-pro/docs/refs/heads/main/logos/square-nobg.png"> Destroy Finished
370899
370984
  </h1>
370900
370985
  <p><b>TFWorkspace: </b>${item.metadata.name}</p>
370901
370986
 
370902
370987
  <details id=github>
370903
- <summary>DESTROY LOGS ${dividedOutput.length > 1 ? '(Part ' + currentCommentNo + ')' : ''}</summary>
370988
+ <summary>DESTROY LOGS${partIndicator}</summary>
370904
370989
 
370905
370990
  \`\`\`shell
370906
370991
  ${commentContent}
370907
370992
  \`\`\`
370908
370993
  </details>`;
370909
- operator_src_logger.debug(`The user feedback for item '${item.kind}/${item.metadata.name}' is being published as a comment on pull request '${lastPr.number}' for repository '${repo}' in organization '${org}'.`);
370910
- await github_0.pulls.commentInPR(comment, lastPr.number, repo, org, 'tfworkspace:destroy');
370911
- operator_src_logger.debug(`The user feedback for the '${item.kind}/${item.metadata.name}' destroy operation is being published as a comment on pull request '${lastPr.number}'.`);
370912
- currentCommentNo += 1;
370913
- }
370994
+ });
370995
+ operator_src_logger.debug(`The user feedback for item '${item.kind}/${item.metadata.name}' is being published as a comment on pull request '${lastPr.number}' for repository '${repo}' in organization '${org}'.`);
370996
+ // Get octokit instance for the org to use with upsertMultiPartStickyComments
370997
+ const octokit = await github_0.auth.getOctokitForOrg(org);
370998
+ await github_0.feedback.upsertMultiPartStickyComments(octokit, {
370999
+ owner: org,
371000
+ repo,
371001
+ pullNumber: lastPr.number,
371002
+ baseKind: 'tfworkspace:destroy',
371003
+ bodies: commentBodies,
371004
+ });
371005
+ operator_src_logger.debug(`The user feedback for the '${item.kind}/${item.metadata.name}' destroy operation has been published as a comment on pull request '${lastPr.number}'.`);
370914
371006
  }
370915
371007
  catch (e) {
370916
371008
  operator_src_logger.error(`An error occurred while publishing user feedback for item '${item.kind}/${item.metadata.name}': '${e}'.`);
@@ -370919,23 +371011,31 @@ ${commentContent}
370919
371011
  async function publishApply(item, applyOutput, kind) {
370920
371012
  const { prNumber, repo, org } = extractPrInfo(item);
370921
371013
  const dividedOutput = github_0.pulls.divideCommentIntoChunks(applyOutput, 250);
370922
- let currentCommentNo = 1;
370923
- for (const commentContent of dividedOutput) {
370924
- const comment = `<h1>
371014
+ const commentBodies = dividedOutput.map((commentContent, index) => {
371015
+ const isMultiPart = dividedOutput.length > 1;
371016
+ const partIndicator = isMultiPart ? ` (Part ${index + 1})` : '';
371017
+ return `<h1>
370925
371018
  <img width="25" src="https://raw.githubusercontent.com/firestartr-pro/docs/refs/heads/main/logos/square-nobg.png"> Apply Finished
370926
371019
  </h1>
370927
371020
  <p><b>${kind}: </b>${item.metadata.name}</p>
370928
371021
 
370929
371022
  <details id=github>
370930
- <summary>APPLY LOGS ${dividedOutput.length > 1 ? '(Part ' + currentCommentNo + ')' : ''}</summary>
371023
+ <summary>APPLY LOGS${partIndicator}</summary>
370931
371024
 
370932
371025
  \`\`\`shell
370933
371026
  ${commentContent}
370934
371027
  \`\`\`
370935
371028
  </details>`;
370936
- await github_0.pulls.commentInPR(comment, prNumber, repo, org, 'apply');
370937
- currentCommentNo += 1;
370938
- }
371029
+ });
371030
+ // Get octokit instance for the org to use with upsertMultiPartStickyComments
371031
+ const octokit = await github_0.auth.getOctokitForOrg(org);
371032
+ await github_0.feedback.upsertMultiPartStickyComments(octokit, {
371033
+ owner: org,
371034
+ repo,
371035
+ pullNumber: prNumber,
371036
+ baseKind: `apply:${kind}`,
371037
+ bodies: commentBodies,
371038
+ });
370939
371039
  }
370940
371040
  function tryCreateErrorSummary(title, errorMsg) {
370941
371041
  try {
@@ -370993,23 +371093,31 @@ ${message}
370993
371093
  async function publishPlan(item, planOutput, prNumber, repo, org) {
370994
371094
  try {
370995
371095
  const dividedOutput = github_0.pulls.divideCommentIntoChunks(planOutput, 250);
370996
- let currentCommentNo = 1;
370997
- for (const commentContent of dividedOutput) {
370998
- const comment = `<h1>
371096
+ const commentBodies = dividedOutput.map((commentContent, index) => {
371097
+ const isMultiPart = dividedOutput.length > 1;
371098
+ const partIndicator = isMultiPart ? ` (Part ${index + 1})` : '';
371099
+ return `<h1>
370999
371100
  <img width="25" src="https://raw.githubusercontent.com/firestartr-pro/docs/refs/heads/main/logos/square-nobg.png"> Plan Finished
371000
371101
  </h1>
371001
371102
  <p><b>TFWorkspace: </b>${item.metadata.name}</p>
371002
371103
 
371003
371104
  <details id=github>
371004
- <summary>PLAN LOGS ${dividedOutput.length > 1 ? '(Part ' + currentCommentNo + ')' : ''}</summary>
371105
+ <summary>PLAN LOGS${partIndicator}</summary>
371005
371106
 
371006
371107
  \`\`\`shell
371007
371108
  ${commentContent}
371008
371109
  \`\`\`
371009
371110
  </details>`;
371010
- await github_0.pulls.commentInPR(comment, prNumber, repo, org, 'tfworkspace:plan');
371011
- currentCommentNo += 1;
371012
- }
371111
+ });
371112
+ // Get octokit instance for the org to use with upsertMultiPartStickyComments
371113
+ const octokit = await github_0.auth.getOctokitForOrg(org);
371114
+ await github_0.feedback.upsertMultiPartStickyComments(octokit, {
371115
+ owner: org,
371116
+ repo,
371117
+ pullNumber: prNumber,
371118
+ baseKind: 'tfworkspace:plan',
371119
+ bodies: commentBodies,
371120
+ });
371013
371121
  }
371014
371122
  catch (e) {
371015
371123
  console.error(e);
@@ -371050,6 +371158,51 @@ function helperCreateCheckRunName(cmd, item) {
371050
371158
  return `${item.kind} - ${cmd}`;
371051
371159
  }
371052
371160
 
371161
+ ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
371162
+ const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
371163
+ function replaceConfigSecrets(config, secrets) {
371164
+ for (const key in config) {
371165
+ if (typeof config[key] === 'object' && config[key] !== null) {
371166
+ // If the property is an object, call this function recursively
371167
+ replaceConfigSecrets(config[key], secrets);
371168
+ }
371169
+ else if (typeof config[key] === 'string') {
371170
+ // If the property is a string and its value is equal to secrets.something,
371171
+ // replace the value with the value of the 'something' key in the secrets object
371172
+ config[key] = config[key].replace(secretRegex, (_, group1) => {
371173
+ if (!secrets[group1]) {
371174
+ throw new Error(`Secret ${group1} not found in secrets`);
371175
+ }
371176
+ return secrets[group1];
371177
+ });
371178
+ }
371179
+ }
371180
+ return config;
371181
+ }
371182
+ function replaceInlineSecrets(inline, secrets) {
371183
+ if (typeof inline !== 'string' || !inline)
371184
+ return inline;
371185
+ let result = inline;
371186
+ result = result.replace(secretRegex, (_, group1) => {
371187
+ if (!secrets[group1]) {
371188
+ throw new Error(`Secret ${group1} not found in secrets`);
371189
+ }
371190
+ return secrets[group1];
371191
+ });
371192
+ return result;
371193
+ }
371194
+ /**
371195
+ * Retrieves a policy annotation value from a custom resource
371196
+ * @param item - The CR to get the policy from
371197
+ * @param annotation - The annotation key to retrieve
371198
+ * @returns The policy value, or undefined if not set
371199
+ */
371200
+ function getPolicy(item, annotation) {
371201
+ const policy = item.metadata.annotations && item.metadata.annotations[annotation];
371202
+ if (policy)
371203
+ return policy;
371204
+ }
371205
+
371053
371206
  ;// CONCATENATED MODULE: ../operator/cdktf.ts
371054
371207
 
371055
371208
 
@@ -371059,9 +371212,17 @@ function helperCreateCheckRunName(cmd, item) {
371059
371212
 
371060
371213
 
371061
371214
 
371215
+
371216
+ const cdktf_LAST_STATE_PR_ANNOTATION = 'firestartr.dev/last-state-pr';
371062
371217
  function processOperation(item, op, handler) {
371063
371218
  operator_src_logger.info(`Processing operation ${op} on ${item.kind}/${item.metadata?.name}`);
371064
371219
  try {
371220
+ const policy = getPolicy(item, 'firestartr.dev/policy');
371221
+ // If general policy is observe/observe-only, route to observe mode instead of apply
371222
+ if (!policy || policy === 'observe' || policy === 'observe-only') {
371223
+ operator_src_logger.info(`Policy is '${policy || 'not set (default)'}', routing to observe mode`);
371224
+ return cdktf_observe(item, op, handler);
371225
+ }
371065
371226
  switch (op) {
371066
371227
  case OperationType.UPDATED:
371067
371228
  return updated(item, op, handler);
@@ -371086,6 +371247,11 @@ function processOperation(item, op, handler) {
371086
371247
  throw e;
371087
371248
  }
371088
371249
  }
371250
+ async function* cdktf_observe(item, op, handler) {
371251
+ for await (const transition of doPlan(item, op, handler)) {
371252
+ yield transition;
371253
+ }
371254
+ }
371089
371255
  async function* created(item, op, handler) {
371090
371256
  for await (const transition of doApply(item, op, handler)) {
371091
371257
  yield transition;
@@ -371121,8 +371287,18 @@ async function* sync(item, op, handler) {
371121
371287
  status: 'False',
371122
371288
  message: 'Synth CDKTF',
371123
371289
  };
371124
- for await (const transition of doApply(item, op, handler)) {
371125
- yield transition;
371290
+ const syncPolicy = getPolicy(item, 'firestartr.dev/sync-policy');
371291
+ if (syncPolicy === 'apply') {
371292
+ operator_src_logger.info(`SYNC OPERATION: applying item ${item.metadata.name} with sync-policy=${syncPolicy}`);
371293
+ for await (const transition of doApply(item, op, handler)) {
371294
+ yield transition;
371295
+ }
371296
+ }
371297
+ else {
371298
+ operator_src_logger.info(`SYNC OPERATION: planning item ${item.metadata.name} with sync-policy=${syncPolicy || 'default (observe)'}`);
371299
+ for await (const transition of doPlan(item, op, handler)) {
371300
+ yield transition;
371301
+ }
371126
371302
  }
371127
371303
  yield {
371128
371304
  item,
@@ -371161,15 +371337,14 @@ async function* markedToDeletion(item, op, handler) {
371161
371337
  message: 'Destroying process started',
371162
371338
  };
371163
371339
  const deps = await handler.resolveReferences();
371164
- const annotation = 'firestartr.dev/last-state-pr';
371165
- const statePr = item?.metadata?.annotations?.[annotation];
371340
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371166
371341
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371167
371342
  if (!hasStatePr) {
371168
371343
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371169
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371344
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371170
371345
  }
371171
371346
  else {
371172
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371347
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371173
371348
  }
371174
371349
  const destroyOutput = await provisioner.runProvisioner({
371175
371350
  mainCr: item,
@@ -371204,7 +371379,7 @@ async function* markedToDeletion(item, op, handler) {
371204
371379
  };
371205
371380
  await handler.finalize(handler.pluralKind, item.metadata.namespace, item, 'firestartr.dev/finalizer');
371206
371381
  await handler.writeTerraformOutputInTfResult(item, output);
371207
- if (item.metadata.annotations['firestartr.dev/last-state-pr'] || false) {
371382
+ if (item.metadata.annotations[cdktf_LAST_STATE_PR_ANNOTATION] || false) {
371208
371383
  await addDestroyCommitStatus(item, 'success', 'Destroy operation completed', `Terraform Destroy ${item.metadata.name}`);
371209
371384
  }
371210
371385
  void handler.success();
@@ -371285,15 +371460,14 @@ async function* doApply(item, op, handler) {
371285
371460
  }
371286
371461
  const deps = await handler.resolveReferences();
371287
371462
  operator_src_logger.info(`Item ${item.metadata.name} has the following dependencies: ${deps}`);
371288
- const annotation = 'firestartr.dev/last-state-pr';
371289
- const statePr = item?.metadata?.annotations?.[annotation];
371463
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371290
371464
  const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371291
371465
  if (!hasStatePr) {
371292
371466
  operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371293
- `has no "${annotation}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371467
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs (synth, terraform apply).`);
371294
371468
  }
371295
371469
  else {
371296
- operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${annotation}" = ${statePr}`);
371470
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371297
371471
  }
371298
371472
  const applyOutput = await provisioner.runProvisioner({
371299
371473
  mainCr: item,
@@ -371389,6 +371563,219 @@ async function* doApply(item, op, handler) {
371389
371563
  }
371390
371564
  }
371391
371565
  }
371566
+ async function* doPlan(item, op, handler) {
371567
+ let checkRunCtl;
371568
+ try {
371569
+ cleanTerraformState();
371570
+ yield {
371571
+ item,
371572
+ reason: op,
371573
+ type: 'PLANNING',
371574
+ status: 'True',
371575
+ message: 'Planning process started',
371576
+ };
371577
+ const deps = await handler.resolveReferences();
371578
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371579
+ const hasStatePr = typeof statePr === 'string' && statePr.trim().length > 0;
371580
+ if (!hasStatePr) {
371581
+ operator_src_logger.warn(`CR ${item?.kind ?? 'UnknownKind'}/${item?.metadata?.name ?? 'unknown'} ` +
371582
+ `has no "${cdktf_LAST_STATE_PR_ANNOTATION}" annotation; skipping GitHub Check Runs for plan.`);
371583
+ }
371584
+ else {
371585
+ operator_src_logger.debug(`CR ${item.kind}/${item.metadata.name} uses "${cdktf_LAST_STATE_PR_ANNOTATION}" = ${statePr}`);
371586
+ await addPlanStatusCheck(statePr, 'CDKTF plan in progress...');
371587
+ }
371588
+ // Run provisioner in plan-only mode
371589
+ const planResult = await provisioner.runProvisioner({ mainCr: item, deps }, {
371590
+ planOnly: true,
371591
+ delete: 'deletionTimestamp' in item.metadata,
371592
+ ...(hasStatePr
371593
+ ? {
371594
+ logStreamCallbacksCDKTF: {
371595
+ prepare: async () => {
371596
+ checkRunCtl = await GHCheckRun('synth', item);
371597
+ return checkRunCtl;
371598
+ },
371599
+ },
371600
+ logStreamCallbacksTF: {
371601
+ prepare: async () => {
371602
+ checkRunCtl = await GHCheckRun('plan', item);
371603
+ return checkRunCtl;
371604
+ },
371605
+ },
371606
+ }
371607
+ : {}),
371608
+ });
371609
+ const planOutput = planResult?.output || '';
371610
+ // Parse terraform plan output to detect changes
371611
+ // Handles multiple Terraform output formats and versions
371612
+ const hasChanges = detectPlanChanges(planOutput);
371613
+ if (hasChanges) {
371614
+ yield {
371615
+ item,
371616
+ reason: op,
371617
+ type: 'OUT_OF_SYNC',
371618
+ status: 'True',
371619
+ message: 'Plan has changes',
371620
+ };
371621
+ yield {
371622
+ item,
371623
+ reason: op,
371624
+ type: 'PROVISIONED',
371625
+ status: 'False',
371626
+ message: 'Plan has changes',
371627
+ };
371628
+ }
371629
+ else {
371630
+ yield {
371631
+ item,
371632
+ reason: op,
371633
+ type: 'OUT_OF_SYNC',
371634
+ status: 'False',
371635
+ message: 'Plan has no changes',
371636
+ };
371637
+ yield {
371638
+ item,
371639
+ reason: op,
371640
+ type: 'PROVISIONED',
371641
+ status: 'True',
371642
+ message: 'Plan has no changes',
371643
+ };
371644
+ }
371645
+ // Store plan details for later reference
371646
+ yield {
371647
+ item,
371648
+ reason: op,
371649
+ type: 'LAST_PLAN_DETAILS',
371650
+ status: 'Unknown',
371651
+ message: planOutput,
371652
+ };
371653
+ yield {
371654
+ item,
371655
+ reason: op,
371656
+ type: 'PLANNING',
371657
+ status: 'False',
371658
+ message: 'Planning process finished',
371659
+ };
371660
+ if (hasStatePr) {
371661
+ await addPlanStatusCheck(statePr, hasChanges ? 'Plan has changes' : 'Plan has no changes', 'completed');
371662
+ }
371663
+ }
371664
+ catch (e) {
371665
+ operator_src_logger.error(`CDKTF plan failed: ${e}`);
371666
+ if (checkRunCtl) {
371667
+ checkRunCtl.fnOnError(e);
371668
+ }
371669
+ yield {
371670
+ item,
371671
+ reason: op,
371672
+ type: 'ERROR',
371673
+ status: 'True',
371674
+ message: e.toString(),
371675
+ };
371676
+ yield {
371677
+ item,
371678
+ reason: op,
371679
+ type: 'PLANNING',
371680
+ status: 'False',
371681
+ message: e.toString(),
371682
+ };
371683
+ yield {
371684
+ item,
371685
+ reason: op,
371686
+ type: 'PROVISIONED',
371687
+ status: 'False',
371688
+ message: e.toString(),
371689
+ };
371690
+ const statePr = item?.metadata?.annotations?.[cdktf_LAST_STATE_PR_ANNOTATION];
371691
+ if (statePr) {
371692
+ const summaryText = tryCreateErrorSummary('CDKTF Plan failed', e);
371693
+ await addPlanStatusCheck(statePr, summaryText, 'completed', true);
371694
+ }
371695
+ await handler.writeTerraformOutputInTfResult(item, e);
371696
+ void handler.error();
371697
+ }
371698
+ }
371699
+ /**
371700
+ * Detects if a Terraform plan output contains changes
371701
+ * Handles multiple Terraform versions and output formats
371702
+ * @param planOutput - The text output from terraform plan
371703
+ * @returns true if changes are detected, false otherwise
371704
+ */
371705
+ function detectPlanChanges(planOutput) {
371706
+ if (!planOutput || planOutput.trim().length === 0) {
371707
+ return false;
371708
+ }
371709
+ // Normalize the output for consistent matching
371710
+ const normalized = planOutput.toLowerCase();
371711
+ // Pattern 1: "Plan: X to add, Y to change, Z to destroy"
371712
+ // Matches: "Plan: 1 to add, 0 to change, 0 to destroy"
371713
+ const planPattern = /plan:\s*(\d+)\s+to\s+add,\s*(\d+)\s+to\s+change,\s*(\d+)\s+to\s+destroy/i;
371714
+ const planMatch = planOutput.match(planPattern);
371715
+ if (planMatch) {
371716
+ const add = Number(planMatch[1]);
371717
+ const change = Number(planMatch[2]);
371718
+ const destroy = Number(planMatch[3]);
371719
+ if (add > 0 || change > 0 || destroy > 0) {
371720
+ return true;
371721
+ }
371722
+ // Explicitly found "Plan:" with 0/0/0 - no changes
371723
+ return false;
371724
+ }
371725
+ // Pattern 2: Individual change indicators
371726
+ // Handles variations like "1 to add", "2 to change", "3 to destroy"
371727
+ const hasAdditions = /\d+\s+to\s+add/i.test(planOutput);
371728
+ const hasChanges = /\d+\s+to\s+change/i.test(planOutput);
371729
+ const hasDestructions = /\d+\s+to\s+destroy/i.test(planOutput);
371730
+ const hasImports = /\d+\s+to\s+import/i.test(planOutput);
371731
+ if (hasAdditions || hasChanges || hasDestructions || hasImports) {
371732
+ return true;
371733
+ }
371734
+ // Pattern 3: Resource-level change indicators
371735
+ // Matches: "# resource will be created", "# resource will be updated", etc.
371736
+ const resourceChangePatterns = [
371737
+ /will\s+be\s+(created|destroyed|updated|replaced)/i,
371738
+ /must\s+be\s+(created|destroyed|updated|replaced)/i,
371739
+ /#.*\s+(create|destroy|update|replace)/i,
371740
+ ];
371741
+ for (const pattern of resourceChangePatterns) {
371742
+ if (pattern.test(planOutput)) {
371743
+ return true;
371744
+ }
371745
+ }
371746
+ // Pattern 4: Action symbols in plan output
371747
+ // Terraform uses symbols like +, -, ~, -/+ to indicate changes
371748
+ const actionSymbols = [
371749
+ /^\s*[+]\s+/m,
371750
+ /^\s*[-]\s+/m,
371751
+ /^\s*[~]\s+/m,
371752
+ /^\s*[-][/][+]\s+/m, // Replace
371753
+ ];
371754
+ for (const pattern of actionSymbols) {
371755
+ if (pattern.test(planOutput)) {
371756
+ return true;
371757
+ }
371758
+ }
371759
+ // Pattern 5: No changes messages (inverse check)
371760
+ const noChangesPatterns = [
371761
+ /no\s+changes/i,
371762
+ /infrastructure\s+is\s+up[-\s]to[-\s]date/i,
371763
+ /your\s+infrastructure\s+matches\s+the\s+configuration/i,
371764
+ /0\s+to\s+add,\s*0\s+to\s+change,\s*0\s+to\s+destroy/i,
371765
+ ];
371766
+ for (const pattern of noChangesPatterns) {
371767
+ if (pattern.test(planOutput)) {
371768
+ return false;
371769
+ }
371770
+ }
371771
+ // If we find "Plan:" keyword but couldn't parse it, log a warning and assume no changes
371772
+ if (normalized.includes('plan:')) {
371773
+ operator_src_logger.warn('Found "Plan:" in output but could not parse change counts. Assuming no changes.');
371774
+ return false;
371775
+ }
371776
+ // Default: assume no changes if we can't detect any
371777
+ return false;
371778
+ }
371392
371779
  function cleanTerraformState() {
371393
371780
  external_fs_.rmSync('/library/packages/provisioner/cdktf.out', {
371394
371781
  recursive: true,
@@ -372388,40 +372775,6 @@ function tf_checkrun_helperCreateCheckRunName(cmd) {
372388
372775
  return `TFWorkspace - ${cmd}`;
372389
372776
  }
372390
372777
 
372391
- ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
372392
- const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
372393
- function replaceConfigSecrets(config, secrets) {
372394
- for (const key in config) {
372395
- if (typeof config[key] === 'object' && config[key] !== null) {
372396
- // If the property is an object, call this function recursively
372397
- replaceConfigSecrets(config[key], secrets);
372398
- }
372399
- else if (typeof config[key] === 'string') {
372400
- // If the property is a string and its value is equal to secrets.something,
372401
- // replace the value with the value of the 'something' key in the secrets object
372402
- config[key] = config[key].replace(secretRegex, (_, group1) => {
372403
- if (!secrets[group1]) {
372404
- throw new Error(`Secret ${group1} not found in secrets`);
372405
- }
372406
- return secrets[group1];
372407
- });
372408
- }
372409
- }
372410
- return config;
372411
- }
372412
- function replaceInlineSecrets(inline, secrets) {
372413
- if (typeof inline !== 'string' || !inline)
372414
- return inline;
372415
- let result = inline;
372416
- result = result.replace(secretRegex, (_, group1) => {
372417
- if (!secrets[group1]) {
372418
- throw new Error(`Secret ${group1} not found in secrets`);
372419
- }
372420
- return secrets[group1];
372421
- });
372422
- return result;
372423
- }
372424
-
372425
372778
  ;// CONCATENATED MODULE: ../operator/src/tfworkspaces/process-operation.ts
372426
372779
 
372427
372780
 
@@ -372612,11 +372965,6 @@ async function* process_operation_renamed(item, op, handler) {
372612
372965
  yield transition;
372613
372966
  }
372614
372967
  }
372615
- function getPolicy(item, annotation) {
372616
- const policy = item.metadata.annotations && item.metadata.annotations[annotation];
372617
- if (policy)
372618
- return policy;
372619
- }
372620
372968
  async function* process_operation_updated(item, op, handler) {
372621
372969
  for await (const transition of process_operation_doApply(item, op, handler)) {
372622
372970
  yield transition;
@@ -373260,7 +373608,7 @@ const processOperationPlan_TF_PROJECTS_PATH = '/tmp/tfworkspaces';
373260
373608
  function processOperationPlan(item, op, handler) {
373261
373609
  try {
373262
373610
  processOperationPlan_clearLocalTfProjects();
373263
- const policy = processOperationPlan_getPolicy(item);
373611
+ const policy = getPolicy(item, 'firestartr.dev/policy');
373264
373612
  if (policy === 'observe' || policy === 'apply') {
373265
373613
  return processOperationPlan_plan(item, op, handler);
373266
373614
  }
@@ -373707,12 +374055,6 @@ function processOperationPlan_getErrorOutputMessage(cr, key, ref) {
373707
374055
  throw new Error(`❌ Source ${cr.spec.source} not supported`);
373708
374056
  }
373709
374057
  }
373710
- function processOperationPlan_getPolicy(item) {
373711
- const policy = item.metadata.annotations &&
373712
- item.metadata.annotations['firestartr.dev/policy'];
373713
- if (policy)
373714
- return policy;
373715
- }
373716
374058
 
373717
374059
  ;// CONCATENATED MODULE: ../operator/src/ctx.ts
373718
374060
  class Ctx {