@firestartr/cli 2.11.0-snapshot-patches-patch-claims-1 → 2.11.0-snapshot-features-traceability-00

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
@@ -128395,7 +128395,7 @@ function trasformLeg(leg) {
128395
128395
 
128396
128396
 
128397
128397
  function getFeatureRenderedPathForEntity(entity, featureName, basePath = '/tmp') {
128398
- const entityFolderName = `${entity.metadata?.name ?? entity.name}`.toLowerCase();
128398
+ const entityFolderName = `${entity.metadata.name}`.toLowerCase();
128399
128399
  return external_path_default().join(basePath, entityFolderName, featureName);
128400
128400
  }
128401
128401
  function getFeatureRenderedConfigForComponent(entity, featureName, basePath = '/tmp/features') {
@@ -164379,6 +164379,771 @@ const base_secrets_schema = 'firestartr.dev://common/SecretsClaim';
164379
164379
  SecretsClaimSchema: base_secrets_schema,
164380
164380
  });
164381
164381
 
164382
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
164383
+
164384
+
164385
+
164386
+
164387
+
164388
+
164389
+
164390
+
164391
+
164392
+
164393
+
164394
+
164395
+
164396
+
164397
+
164398
+
164399
+ // We need to ensure the grep command is a gnu version
164400
+ let IS_GNU_GREP = false;
164401
+ let GREP_ON_CHECK = false;
164402
+ // this loop is for avoid race conditions
164403
+ // once a check is running no more checks
164404
+ async function waitForCheck() {
164405
+ const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
164406
+ do {
164407
+ await f_wait();
164408
+ } while (GREP_ON_CHECK);
164409
+ }
164410
+ function checkGrep() {
164411
+ return new Promise((ok, ko) => {
164412
+ // idempotency: already checked
164413
+ if (IS_GNU_GREP)
164414
+ return ok();
164415
+ // a check is already running no new checks
164416
+ if (GREP_ON_CHECK)
164417
+ return waitForCheck();
164418
+ logger.info('Checking the grep command');
164419
+ GREP_ON_CHECK = true;
164420
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
164421
+ handler.on('close', (code) => {
164422
+ GREP_ON_CHECK = false;
164423
+ if (code === 0) {
164424
+ logger.debug('Grep is a gnu grep');
164425
+ IS_GNU_GREP = true;
164426
+ ok();
164427
+ }
164428
+ else {
164429
+ ko('Lazy evaluation needs a gnu-grep command');
164430
+ }
164431
+ });
164432
+ });
164433
+ }
164434
+ async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
164435
+ await checkGrep();
164436
+ let result = existingRefs;
164437
+ logger.info(`Load reference ${claimRef}`);
164438
+ initVirtualClaims(org);
164439
+ logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
164440
+ // cargas datos con grep
164441
+ try {
164442
+ const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
164443
+ const claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
164444
+ let variants = [];
164445
+ if (claim.kind === 'TFWorkspaceClaim' &&
164446
+ claim.providers?.terraform?.variants) {
164447
+ variants = claim.providers.terraform.variants;
164448
+ delete claim.providers.terraform.variants;
164449
+ }
164450
+ logger.silly(`Patched claim is:
164451
+ ---
164452
+ ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
164453
+ try {
164454
+ validateClaim(claim, base[`${claim.kind}Schema`]);
164455
+ await optionalValidation(claim);
164456
+ }
164457
+ catch (error) {
164458
+ let errorMsg = '';
164459
+ if (Array.isArray(error)) {
164460
+ for (const data of error) {
164461
+ errorMsg = `${errorMsg}
164462
+ - ${data.instancePath}: ${data.message}`;
164463
+ }
164464
+ }
164465
+ else {
164466
+ errorMsg = error instanceof Error ? error.message : String(error);
164467
+ }
164468
+ throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
164469
+ }
164470
+ result[claimRef] = {};
164471
+ result[claimRef]['claim'] = claim;
164472
+ result[claimRef]['claimPath'] = VisitedClaims[claimRef];
164473
+ if (VisitedClaims[claimRef] === 'virtual') {
164474
+ result = await setVirtualClaimAdditionalData(result, claim, claimRef);
164475
+ }
164476
+ else {
164477
+ result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164478
+ }
164479
+ const claimKind = claim.kind;
164480
+ const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
164481
+ for (const ref of references) {
164482
+ if (!result[ref]) {
164483
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164484
+ result = lodash_default().merge(result, resolvedReferences);
164485
+ }
164486
+ }
164487
+ if (variants.length > 0) {
164488
+ const parentClaimPath = VisitedClaims[claimRef];
164489
+ for (const variant of variants) {
164490
+ if (!variant.name || !variant.overrides) {
164491
+ throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
164492
+ }
164493
+ const prohibitedOverrideFields = ['source', 'module', 'name'];
164494
+ for (const field of prohibitedOverrideFields) {
164495
+ if (field in variant.overrides) {
164496
+ throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
164497
+ }
164498
+ }
164499
+ const composedName = claim.providers.terraform.name + '-' + variant.name;
164500
+ const variantClaim = lodash_default().cloneDeep(claim);
164501
+ variantClaim._parentClaimName = claim.name;
164502
+ variantClaim.name = composedName;
164503
+ variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
164504
+ variantClaim.providers.terraform.name = composedName;
164505
+ if (!variantClaim.annotations) {
164506
+ variantClaim.annotations = {};
164507
+ }
164508
+ variantClaim.annotations['firestartr.dev/variant-of'] =
164509
+ claim.providers.terraform.name;
164510
+ const variantRef = `TFWorkspaceClaim-${composedName}`;
164511
+ if (result[variantRef]) {
164512
+ throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
164513
+ }
164514
+ logger.info(`Creating synthetic variant claim ${variantRef}`);
164515
+ result[variantRef] = {};
164516
+ result[variantRef]['claim'] = variantClaim;
164517
+ result[variantRef]['claimPath'] = parentClaimPath;
164518
+ result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164519
+ const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
164520
+ for (const ref of variantReferences) {
164521
+ if (!result[ref]) {
164522
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164523
+ result = lodash_default().merge(result, resolvedReferences);
164524
+ }
164525
+ }
164526
+ }
164527
+ }
164528
+ }
164529
+ catch (err) {
164530
+ throw `Lazy Loading: ${err}`;
164531
+ }
164532
+ return [result, postValidations];
164533
+ }
164534
+ let LoadedClaims = {};
164535
+ let VisitedClaims = {};
164536
+ let DuplicatedClaims = {};
164537
+ function resetLazyLoader() {
164538
+ LoadedClaims = {};
164539
+ VisitedClaims = {};
164540
+ DuplicatedClaims = {};
164541
+ }
164542
+ async function lazyGetClaim(kind, name, org, cwd) {
164543
+ const indice = `${kind}-${name}`;
164544
+ logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
164545
+ if (indice in LoadedClaims)
164546
+ return LoadedClaims[indice];
164547
+ if (isVirtualClaim(kind, name)) {
164548
+ await loadVirtualClaim(kind, name, org);
164549
+ }
164550
+ else {
164551
+ await getClaimsByName(name, cwd);
164552
+ }
164553
+ if (indice in LoadedClaims) {
164554
+ return LoadedClaims[indice];
164555
+ }
164556
+ else {
164557
+ throw new Error(`Error: ${kind}-${name} not found`);
164558
+ }
164559
+ }
164560
+ async function getClaimsByName(name, cwd = '.') {
164561
+ return new Promise((ok, ko) => {
164562
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
164563
+ cwd: cwd,
164564
+ });
164565
+ logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
164566
+ const entradas = [];
164567
+ const chunks = [];
164568
+ handler.stdout.on('data', (data) => {
164569
+ chunks.push(data.toString('utf-8'));
164570
+ });
164571
+ handler.on('close', () => {
164572
+ const dataFinal = chunks.join('');
164573
+ const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
164574
+ for (const fileName of fileNameList) {
164575
+ // only yaml files
164576
+ if (!fileName.match(/\.yml$|\.yaml$/)) {
164577
+ continue;
164578
+ }
164579
+ // only entries not already visited
164580
+ if (Object.values(VisitedClaims).includes(fileName)) {
164581
+ continue;
164582
+ }
164583
+ entradas.push(external_path_.join(cwd, fileName));
164584
+ }
164585
+ return Promise.all(entradas.map((entrada) => {
164586
+ return loadRawClaim(entrada);
164587
+ }))
164588
+ .then(() => {
164589
+ ok();
164590
+ })
164591
+ .catch((err) => {
164592
+ ko(err);
164593
+ });
164594
+ });
164595
+ });
164596
+ }
164597
+ async function loadRawClaim(entry) {
164598
+ return new Promise((ok, ko) => {
164599
+ external_fs_.readFile(entry, 'utf-8', (error, data) => {
164600
+ if (error)
164601
+ return ko(`Reading ${entry}: ${error}`);
164602
+ const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164603
+ if (!('kind' in claim && 'name' in claim)) {
164604
+ logger.warn(`Invalid claim file ${entry}`);
164605
+ }
164606
+ else {
164607
+ const claimKey = `${claim.kind}-${claim.name}`;
164608
+ if (Object.keys(DuplicatedClaims).includes(claimKey) &&
164609
+ DuplicatedClaims[claimKey] !== entry) {
164610
+ return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
164611
+ }
164612
+ LoadedClaims[claimKey] = data;
164613
+ VisitedClaims[claimKey] = entry;
164614
+ DuplicatedClaims[claimKey] = entry;
164615
+ ok();
164616
+ }
164617
+ });
164618
+ });
164619
+ }
164620
+ async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
164621
+ const virtualClaim = getVirtualClaim(claim.kind, claim.name);
164622
+ renderedData[claimRef]['initializers'] = [
164623
+ new UUIDInitializer(claim),
164624
+ new InitializerClaimRef(),
164625
+ new InitializerDefault(virtualClaim.getDefaultConfig()),
164626
+ ];
164627
+ renderedData[claimRef]['globals'] = [];
164628
+ renderedData[claimRef]['overrides'] = [];
164629
+ renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
164630
+ return renderedData;
164631
+ }
164632
+ async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
164633
+ renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
164634
+ renderedData[claimRef]['globals'] = await loadGlobals(claim);
164635
+ renderedData[claimRef]['overrides'] = loadOverrides(claim);
164636
+ renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
164637
+ return renderedData;
164638
+ }
164639
+ async function loadVirtualClaim(kind, name, org) {
164640
+ const virtualClaim = getVirtualClaim(kind, name);
164641
+ const expandedClaim = await virtualClaim.expand({ org });
164642
+ logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
164643
+ LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
164644
+ VisitedClaims[`${kind}-${name}`] = 'virtual';
164645
+ }
164646
+
164647
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
164648
+
164649
+
164650
+
164651
+ /*
164652
+ * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
164653
+ * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
164654
+ * - If the block is missing at the claim-level, the default block is applied
164655
+ */
164656
+ const defaultBlocksPaths = ['/providers/terraform/sync'];
164657
+ function applyBlockAwareDefaults(claim, defaultClaim) {
164658
+ // we always work with a clone of the original
164659
+ const claimClone = JSON.parse(JSON.stringify(claim));
164660
+ // if the block is claim-level defined we store it here
164661
+ const defaultBlocks = new Map();
164662
+ // we remove the blocks entirely of the claim
164663
+ // to achieve an add copy
164664
+ for (const defaultBlockPath of defaultBlocksPaths) {
164665
+ if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
164666
+ continue;
164667
+ const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
164668
+ if (originalValue) {
164669
+ logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
164670
+ defaultBlocks.set(defaultBlockPath, originalValue);
164671
+ fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
164672
+ }
164673
+ }
164674
+ // if the patch has the same path of a block
164675
+ // and the block was defined at claim-level
164676
+ // we interchange the value with the original value
164677
+ // otherwise we use the default
164678
+ const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
164679
+ .filter((jp) => jp.op === 'add')
164680
+ .map((jp) => {
164681
+ if (defaultBlocks.has(jp.path)) {
164682
+ return {
164683
+ ...jp,
164684
+ value: defaultBlocks.get(jp.path),
164685
+ };
164686
+ }
164687
+ else {
164688
+ return jp;
164689
+ }
164690
+ });
164691
+ return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
164692
+ }
164693
+ function claimsDefaulter_hasDeepPath(data, deepPath) {
164694
+ return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
164695
+ }
164696
+
164697
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
164698
+
164699
+
164700
+
164701
+
164702
+
164703
+
164704
+
164705
+
164706
+
164707
+
164708
+
164709
+
164710
+
164711
+
164712
+
164713
+ const isYamlFile = new RegExp(/\.(yaml|yml)$/);
164714
+ /**
164715
+ *
164716
+ * @returns Virtual Claims
164717
+ */
164718
+ const virtualClaims = [new FirestartrAllClaim()];
164719
+ /*
164720
+ * Loads all applicable globals for a given claim and stores them into a list.
164721
+ *
164722
+ * Input:
164723
+ * - claim: our custom claim object, with which we check if the global is applicable
164724
+ *
164725
+ * Returns:
164726
+ * - A GlobalSection array promise, containing all the globals loaded for this claim
164727
+ *
164728
+ */
164729
+ async function loadGlobals(claim) {
164730
+ const result = [];
164731
+ await crawl(getPath('globals'), (entry) => {
164732
+ return isYamlFile.test(entry);
164733
+ }, async (entry, data) => {
164734
+ const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164735
+ const ext = external_path_.extname(entry);
164736
+ const name = external_path_.basename(entry, ext);
164737
+ if (name in SECTIONS_BY_FILE_NAME) {
164738
+ const section = new SECTIONS_BY_FILE_NAME[name](globalData);
164739
+ if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
164740
+ throw new Error(`Invalid ${name} global file`);
164741
+ }
164742
+ if (globalData.kind === claim.kind) {
164743
+ result.push(section);
164744
+ }
164745
+ else {
164746
+ logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
164747
+ }
164748
+ }
164749
+ });
164750
+ logger.info(`Loaded globals ${result}`);
164751
+ return result;
164752
+ }
164753
+ /*
164754
+ * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
164755
+ * overrides and stores them into a list. Otherwise, it returns an empty list.
164756
+ *
164757
+ * Input:
164758
+ * - claim: our custom claim object, which should be a GithubRepositoryClaim
164759
+ *
164760
+ * Returns:
164761
+ * - An Overrider array, with a GithubRepositoryOverrider if the claim
164762
+ * was a GithubRepositoryClaim, or empty if it was not.
164763
+ *
164764
+ */
164765
+ function loadOverrides(claim) {
164766
+ const overrides = [];
164767
+ if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
164768
+ overrides.push(new GithubRepositoryOverrider());
164769
+ }
164770
+ return overrides;
164771
+ }
164772
+ /**
164773
+ * Load all normalizers and stores them into a list.
164774
+ * @param claim
164775
+ * @returns
164776
+ */
164777
+ async function loadNormalizers(_claim, path) {
164778
+ const result = [];
164779
+ for (const normalizer of NORMALIZERS) {
164780
+ result.push(new normalizer({ path: path }));
164781
+ }
164782
+ return result;
164783
+ }
164784
+ /*
164785
+ * Loads all applicable initializers for a given claim and stores them into a list.
164786
+ *
164787
+ * Input:
164788
+ * - claim: our custom claim object, with which we check if the initializer is applicable
164789
+ *
164790
+ * Returns:
164791
+ * - An Initializer array promise, containing all the initializers loaded for this claim
164792
+ *
164793
+ */
164794
+ let loadedInitializers = false;
164795
+ async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
164796
+ const result = [];
164797
+ await crawl(initializersPath, (entry) => {
164798
+ return isYamlFile.test(entry);
164799
+ }, async (entry, data) => {
164800
+ const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164801
+ const ext = external_path_.extname(entry);
164802
+ const name = external_path_.basename(entry, ext);
164803
+ if (name in INITIALIZERS_BY_FILE_NAME) {
164804
+ const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
164805
+ if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
164806
+ throw new Error(`Invalid ${name} initializer file`);
164807
+ }
164808
+ if (initializerData.kind === claim.kind) {
164809
+ result.push(initializerBfN);
164810
+ }
164811
+ else {
164812
+ logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
164813
+ }
164814
+ }
164815
+ });
164816
+ for (const initializer of INITIALIZERS) {
164817
+ if (initializer.applicableKinds.includes(claim.kind)) {
164818
+ result.push(new initializer({ path: claimPath }));
164819
+ }
164820
+ }
164821
+ logger.info(`Loaded initializers ${JSON.stringify(result)}`);
164822
+ loadedInitializers = result;
164823
+ return loadedInitializers;
164824
+ }
164825
+ function loadClaimDefaults() {
164826
+ try {
164827
+ return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
164828
+ }
164829
+ catch (error) {
164830
+ const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
164831
+ logger.error(errMsg);
164832
+ throw new Error(errMsg);
164833
+ }
164834
+ }
164835
+ /*
164836
+ * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
164837
+ *
164838
+ * Input:
164839
+ * - claim: our custom claim object, which we want to patch
164840
+ * - defaultsClaims: an object, containing the defaults for each claim kind,
164841
+ * with the same structure that we use for the actual claims
164842
+ *
164843
+ * Returns:
164844
+ * - The patched claim
164845
+ *
164846
+ */
164847
+ function loader_patchClaim(claim, defaultsClaims) {
164848
+ if (defaultsClaims[claim.kind]) {
164849
+ claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
164850
+ }
164851
+ return claim;
164852
+ }
164853
+ async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164854
+ const result = {};
164855
+ const fullResult = {};
164856
+ await crawlWithExclusions(crsPath, (entry) => {
164857
+ return isYamlFile.test(entry);
164858
+ }, async (entry, data) => {
164859
+ const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
164860
+ const metadata = yamlData?.metadata;
164861
+ if (!(yamlData &&
164862
+ typeof yamlData === 'object' &&
164863
+ 'kind' in yamlData &&
164864
+ metadata &&
164865
+ typeof metadata === 'object' &&
164866
+ 'name' in metadata)) {
164867
+ logger.warn(`Invalid CR file ${entry}`);
164868
+ }
164869
+ else {
164870
+ const crKey = `${yamlData.kind}-${metadata.name}`;
164871
+ if (fullResult[crKey]) {
164872
+ logger.error(`Duplicate CR ${crKey}`);
164873
+ }
164874
+ // Keep the complete CR set for validations that need repository-wide context.
164875
+ fullResult[crKey] = yamlData;
164876
+ // Keep the filtered CR set for previous-CR lookup during claim rendering.
164877
+ if (allowedClaimReferences.length === 0 ||
164878
+ allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
164879
+ result[crKey] = yamlData;
164880
+ }
164881
+ }
164882
+ }, excludedPaths.concat(getAdditionalPaths()));
164883
+ return { crs: result, fullCrs: fullResult };
164884
+ }
164885
+ async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164886
+ const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
164887
+ return crs;
164888
+ }
164889
+ /*
164890
+ * Description: Get the renamed using claimRef annotation
164891
+ * Input: claims: any, crs: any
164892
+ * Output: renames: IRenameResult[]
164893
+ */
164894
+ async function loadRenames(claims, crs) {
164895
+ /*
164896
+ * If checkRenames is false, we don't need to check for renames
164897
+ */
164898
+ const renamesEnabled = getRenamesEnabled();
164899
+ if (!renamesEnabled)
164900
+ return [];
164901
+ const result = [];
164902
+ /**
164903
+ * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
164904
+ */
164905
+ for (const cr of Object.values(crs)) {
164906
+ const anyCr = cr;
164907
+ if (!anyCr?.metadata?.annotations)
164908
+ continue;
164909
+ // if the annotation is a claimRef
164910
+ const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
164911
+ const annotations = anyCr?.metadata?.annotations || {};
164912
+ if (annotationName in annotations) {
164913
+ const claimRef = anyCr.metadata.annotations[annotationName];
164914
+ // split the annotation into kind and name
164915
+ const [kind, name] = claimRef.split('/');
164916
+ // if the claim exists, check if the name is different for the given provider
164917
+ const referencedClaim = claims[`${kind}-${name}`].claim;
164918
+ const oldName = anyCr.metadata.name;
164919
+ const newName = referencedClaim.providers[getConfiguredProvider()].name;
164920
+ if (oldName !== newName) {
164921
+ result.push({
164922
+ kind: referencedClaim.kind,
164923
+ claimPath: claims[`${kind}-${name}`].claimPath,
164924
+ claimName: referencedClaim.name,
164925
+ oldName,
164926
+ newName,
164927
+ });
164928
+ }
164929
+ }
164930
+ }
164931
+ return result;
164932
+ }
164933
+ async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
164934
+ const data = {
164935
+ renderClaims: {},
164936
+ crs: {},
164937
+ fullCrs: {},
164938
+ };
164939
+ const defaults = loadClaimDefaults();
164940
+ for await (const claimRef of claimRefList) {
164941
+ const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
164942
+ data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
164943
+ }
164944
+ const crClaimReferences = [];
164945
+ for (const ref of Object.keys(data.renderClaims)) {
164946
+ // Replaces only the first instance of '-'
164947
+ crClaimReferences.push(ref.replace('-', '/'));
164948
+ }
164949
+ const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
164950
+ data['crs'] = loadedCrs.crs;
164951
+ data['fullCrs'] = loadedCrs.fullCrs;
164952
+ return data;
164953
+ }
164954
+ function getOrg() {
164955
+ const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
164956
+ return groupOrg;
164957
+ }
164958
+
164959
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
164960
+ function validateTfStateKeyUniqueness(crs) {
164961
+ const tfStateKeys = {};
164962
+ for (const crKey of Object.keys(crs)) {
164963
+ const cr = crs[crKey];
164964
+ if (!isTerraformWorkspace(cr))
164965
+ continue;
164966
+ const tfKey = cr.spec.firestartr.tfStateKey;
164967
+ if (tfStateKeys[tfKey]) {
164968
+ throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
164969
+
164970
+ The tfStateKey ${tfKey} is not unique,
164971
+
164972
+ It is being used by the following CRs:
164973
+
164974
+ - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
164975
+
164976
+ - ${cr.kind} ${cr.metadata.name}
164977
+
164978
+ Check the following Claims to ensure that the tfStateKey is unique:
164979
+
164980
+ - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
164981
+
164982
+ - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
164983
+ `);
164984
+ }
164985
+ tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
164986
+ }
164987
+ }
164988
+ function references_isCatalogEntity(cr) {
164989
+ return [
164990
+ 'Component',
164991
+ 'Domain',
164992
+ 'System',
164993
+ 'User',
164994
+ 'Group',
164995
+ 'Resource',
164996
+ ].includes(cr.kind);
164997
+ }
164998
+ function isTerraformWorkspace(cr) {
164999
+ return cr.kind === 'FirestartrTerraformWorkspace';
165000
+ }
165001
+
165002
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
165003
+
165004
+ const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
165005
+ function validateCrSizes(crs) {
165006
+ for (const key of Object.keys(crs)) {
165007
+ const cr = crs[key];
165008
+ const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
165009
+ const size = Buffer.byteLength(serialized, 'utf8');
165010
+ if (size > K8S_OBJECT_SIZE_LIMIT) {
165011
+ throw new Error(`CR "${cr.kind}-${cr.metadata.name}" exceeds the Kubernetes object size limit by ${size - K8S_OBJECT_SIZE_LIMIT} bytes. Maximum allowed is ${K8S_OBJECT_SIZE_LIMIT} bytes (1.5MiB).`);
165012
+ }
165013
+ }
165014
+ }
165015
+
165016
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
165017
+
165018
+ function validatePermissionsUniqueness(crs) {
165019
+ for (const crkey in crs) {
165020
+ const cr = crs[crkey];
165021
+ if (cr.kind === 'FirestartrGithubRepository') {
165022
+ const permissions = cr.spec.permissions;
165023
+ logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
165024
+ if (permissions) {
165025
+ const rolesByIdentifier = new Map();
165026
+ for (const perm of permissions) {
165027
+ const identifier = perm.collaborator
165028
+ ? perm.collaborator
165029
+ : `ref:${perm.ref.kind}:${perm.ref.name}`;
165030
+ const role = perm.role;
165031
+ const prevRole = rolesByIdentifier.get(identifier);
165032
+ if (prevRole === undefined) {
165033
+ rolesByIdentifier.set(identifier, role);
165034
+ }
165035
+ else if (prevRole !== role) {
165036
+ throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
165037
+ }
165038
+ }
165039
+ }
165040
+ }
165041
+ }
165042
+ }
165043
+
165044
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
165045
+ const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
165046
+ const CATALOG_KINDS = new Set([
165047
+ 'User',
165048
+ 'Group',
165049
+ 'Component',
165050
+ 'System',
165051
+ 'Domain',
165052
+ 'API',
165053
+ 'Resource',
165054
+ 'CatalogKubernetesResource',
165055
+ 'CatalogTerraformWorkspace',
165056
+ 'CatalogArgoDeploy',
165057
+ 'CatalogOrgWebhook',
165058
+ 'CatalogSecrets',
165059
+ ]);
165060
+ function validateNoBackstageAnnotationsInK8sCrs(crs) {
165061
+ for (const crKey in crs) {
165062
+ const cr = crs[crKey];
165063
+ if (CATALOG_KINDS.has(cr.kind))
165064
+ continue;
165065
+ if (cr.metadata?.annotations) {
165066
+ const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
165067
+ if (backstageKeys.length > 0) {
165068
+ throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
165069
+ `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
165070
+ }
165071
+ }
165072
+ }
165073
+ }
165074
+
165075
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
165076
+
165077
+ /**
165078
+ * @param renderClaims renderClaims to sort
165079
+ * @param kinds kinds to get from renderClaims
165080
+ * @returns renderClaims with only the kinds specified
165081
+ */
165082
+ function sortRenderClaimsByKind(renderClaims, kinds) {
165083
+ let result = {};
165084
+ for (const kind of kinds) {
165085
+ for (const key in renderClaims) {
165086
+ if (renderClaims[key].claim.kind === kind) {
165087
+ result[key] = renderClaims[key];
165088
+ }
165089
+ }
165090
+ result = sortSelfKindRefs(result, kind);
165091
+ }
165092
+ return result;
165093
+ }
165094
+ function sortSelfKindRefs(renderClaims, kind) {
165095
+ if (!hasSelfRefs(kind)) {
165096
+ return renderClaims;
165097
+ }
165098
+ const newRenderClaims = {};
165099
+ const data = extractRefs(renderClaims, kind);
165100
+ const ordered = [];
165101
+ const visiting = [];
165102
+ const visited = [];
165103
+ for (const key of Object.keys(data)) {
165104
+ if (!visited.find((obj) => obj.name === key)) {
165105
+ sortRefs(data, data[key], ordered, visiting, visited);
165106
+ }
165107
+ }
165108
+ for (const cr of ordered) {
165109
+ newRenderClaims[cr.name] = renderClaims[cr.name];
165110
+ }
165111
+ return newRenderClaims;
165112
+ }
165113
+ function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
165114
+ if (visiting.find((obj) => obj.name === claim.name)) {
165115
+ throw new Error(`Circular dependency detected for '${claim.name}'`);
165116
+ }
165117
+ if (!visited.find((obj) => obj.name === claim.name)) {
165118
+ visiting.push(claim);
165119
+ if (claim.refs) {
165120
+ for (const ref of claim.refs) {
165121
+ const referencedObject = data[ref];
165122
+ if (!referencedObject) {
165123
+ throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
165124
+ }
165125
+ sortRefs(data, referencedObject, ordered, visiting, visited);
165126
+ }
165127
+ }
165128
+ visited.push(visiting.pop());
165129
+ ordered.push(claim);
165130
+ }
165131
+ }
165132
+ function hasSelfRefs(kind) {
165133
+ return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
165134
+ }
165135
+
165136
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
165137
+
165138
+ function sortKinds(orderedKinds, claims) {
165139
+ const result = [];
165140
+ for (const kind of orderedKinds) {
165141
+ const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
165142
+ result.push(kindRenderClaims);
165143
+ }
165144
+ return result;
165145
+ }
165146
+
164382
165147
  ;// CONCATENATED MODULE: ../features_renderer/src/common.ts
164383
165148
 
164384
165149
  function isDirectory(path) {
@@ -164419,9 +165184,14 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
164419
165184
  },
164420
165185
  },
164421
165186
  patches: {
164422
- type: 'array',
164423
- items: {
164424
- $ref: '#/definitions/Patch',
165187
+ type: 'object',
165188
+ patternProperties: {
165189
+ '^.+$': {
165190
+ type: 'array',
165191
+ items: {
165192
+ $ref: '#/definitions/Patch',
165193
+ },
165194
+ },
164425
165195
  },
164426
165196
  },
164427
165197
  filesTemplates: {
@@ -164524,7 +165294,7 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
164524
165294
  type: 'string',
164525
165295
  },
164526
165296
  value: {
164527
- type: ['string', 'object', 'array', 'number', 'boolean', 'null'],
165297
+ type: 'string',
164528
165298
  },
164529
165299
  },
164530
165300
  required: ['name', 'op', 'path'],
@@ -164566,12 +165336,6 @@ function __validateFeatureConfig(featurePath) {
164566
165336
  }
164567
165337
  }
164568
165338
  function __validateFeatureConfigData(configData) {
164569
- // Detect old provider-keyed patches shape for migration hint (PLAN #2648)
164570
- if (configData.patches &&
164571
- !Array.isArray(configData.patches) &&
164572
- typeof configData.patches === 'object') {
164573
- throw `Feature: config.yaml invalid patches: expected flat array of patches (claim-relative pointers like /annotations/backstage.io~1techdocs-ref), but got provider-keyed object ${JSON.stringify(Object.keys(configData.patches))}. Migrate: patches: { catalog: [...] } → patches: [{ op, path: /annotations/backstage.io~1techdocs-ref, value }]. See #2648. $ref paths also migrate: [spec, org] → [providers, github, org], [metadata, annotations, firestartr.dev/external-name] → [providers, github, name]`;
164574
- }
164575
165339
  const ajv = new (ajv_default())({
164576
165340
  allowUnionTypes: true,
164577
165341
  });
@@ -164593,12 +165357,14 @@ function stripAnchoringConstraints(schemaDef) {
164593
165357
  return clone;
164594
165358
  }
164595
165359
  function __validateJsonPatches(configData) {
164596
- const patches = configData.patches;
164597
- if (!Array.isArray(patches))
164598
- return;
164599
- const err = fast_json_patch_default().validate(patches);
164600
- if (err) {
164601
- throw `Feature: config.yaml -> patches: invalid patches ${JSON.stringify(err.message ?? err)}`;
165360
+ const keys = Object.keys(configData.patches);
165361
+ for (const key of keys) {
165362
+ if (configData.patches[key]) {
165363
+ if (fast_json_patch_default().validate(configData.patches[key])) {
165364
+ const errors = JSON.stringify(fast_json_patch_default().validate(configData.patches[key]).message);
165365
+ throw `Feature: config.yaml -> patches: invalid patches ${errors}`;
165366
+ }
165367
+ }
164602
165368
  }
164603
165369
  }
164604
165370
 
@@ -165384,12 +166150,19 @@ function addTraceability(context, src, content) {
165384
166150
  }
165385
166151
  function addTraceabilityStamp(context, content) {
165386
166152
  const traceability = context.traceability || {};
165387
- const stamp = `---
165388
- # FEATURE_NAME: ${traceability.name}
165389
- # FEATURE_VERSION: ${traceability.version}
165390
- # FEATURE_URL: ${traceability.url}
165391
-
165392
- `;
166153
+ const stampLines = [
166154
+ '---',
166155
+ `# FEATURE_NAME: ${traceability.name}`,
166156
+ `# FEATURE_VERSION: ${traceability.version}`,
166157
+ `# FEATURE_URL: ${traceability.url}`,
166158
+ ];
166159
+ if (traceability.sha) {
166160
+ stampLines.push(`# FEATURE_GIT_SHA: "${traceability.sha}"`);
166161
+ }
166162
+ if (Array.isArray(traceability.tags) && traceability.tags.length > 0) {
166163
+ stampLines.push(`# FEATURE_GIT_TAGS: [${traceability.tags.join(', ')}]`);
166164
+ }
166165
+ const stamp = `${stampLines.join('\n')}\n\n`;
165393
166166
  const output = [];
165394
166167
  let replaced = false;
165395
166168
  for (const line of content.split(/\n/)) {
@@ -165436,12 +166209,10 @@ const renderTestsSchema = {
165436
166209
  items: {
165437
166210
  type: 'object',
165438
166211
  additionalProperties: false,
165439
- required: ['name'],
165440
- anyOf: [{ required: ['cr'] }, { required: ['claim'] }],
166212
+ required: ['name', 'cr'],
165441
166213
  properties: {
165442
166214
  name: { type: 'string', minLength: 1 },
165443
166215
  cr: { type: 'string', minLength: 1 },
165444
- claim: { type: 'string', minLength: 1 },
165445
166216
  args: { type: 'object' },
165446
166217
  },
165447
166218
  },
@@ -165496,9 +166267,6 @@ function resolveCrPath(featurePath, crRelPath) {
165496
166267
  }
165497
166268
  return resolved;
165498
166269
  }
165499
- function resolveClaimPath(featurePath, claimRelPath) {
165500
- return resolveCrPath(featurePath, claimRelPath);
165501
- }
165502
166270
  function listYamlFiles(dir) {
165503
166271
  if (!fs.existsSync(dir))
165504
166272
  return [];
@@ -165563,6 +166331,7 @@ function buildExpectedOutput(config, renderDir) {
165563
166331
  return {
165564
166332
  files,
165565
166333
  patches: config.patches || [],
166334
+ traceability: {},
165566
166335
  };
165567
166336
  }
165568
166337
  /* ---------- Context-style API for a render temp dir ---------- */
@@ -165616,7 +166385,6 @@ async function createRenderContext(prefix = 'feature-render-') {
165616
166385
  buildExpectedOutput,
165617
166386
  loadAndValidateRenderTests,
165618
166387
  resolveCrPath,
165619
- resolveClaimPath,
165620
166388
  });
165621
166389
 
165622
166390
  ;// CONCATENATED MODULE: ../features_renderer/src/featureArgsValidation.ts
@@ -165734,103 +166502,26 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
165734
166502
  // Not used anymore
165735
166503
  // output.unpatches = context.config.unpatches
165736
166504
  });
166505
+ output.traceability = context.traceability;
165737
166506
  external_fs_default().writeFileSync(external_path_default().join(featureRenderPath, 'config.json'), JSON.stringify(context.config));
165738
166507
  // write the output file
165739
166508
  const configFile = external_path_default().join(featureRenderPath, 'output.json');
165740
166509
  external_fs_default().writeFileSync(configFile, JSON.stringify(output));
165741
166510
  return output;
165742
166511
  }
165743
- function getPatches(featurePath, entity, firestartrConfig = {}, featureArgs = {}) {
165744
- const configData = validate(featurePath);
165745
- validateFeatureArgs(featurePath, featureArgs);
165746
- const context = buildContext(entity, configData.args, firestartrConfig, featureArgs);
165747
- const rendered = JSON.parse(renderContent(JSON.stringify(configData), context));
165748
- return rendered.patches ?? [];
165749
- }
165750
- function buildContext(stitchedClaim, args, firestartrConfig, featureArgs) {
166512
+ function buildContext(entity, args, firestartrConfig, featureArgs) {
165751
166513
  const context = {};
165752
- const resolveRef = (doc, ref) => {
165753
- const v = lodash_default().get(doc, ref);
165754
- if (v !== undefined)
165755
- return v;
165756
- // Fallback for transition: try CR-shaped alternatives when stitchedClaim is CR-shaped
165757
- // and vice versa. Covers the ADR 0006 table plus legacy feature_a provisioner paths.
165758
- const altTable = {
165759
- 'providers,github,org': [
165760
- ['spec', 'org'],
165761
- ['spec', 'provisioner', 'org'],
165762
- ['spec', 'firestartr', 'technology', 'stack'],
165763
- ],
165764
- 'providers,github,branchStrategy,defaultBranch': [
165765
- ['spec', 'repo', 'defaultBranch'],
165766
- ['spec', 'provisioner', 'repo', 'defaultBranch'],
165767
- ],
165768
- 'providers,github,name': [
165769
- ['metadata', 'annotations', 'firestartr.dev/external-name'],
165770
- ['metadata', 'name'],
165771
- ],
165772
- name: [['metadata', 'name']],
165773
- 'annotations,firestartr.dev/dest-annotation': [
165774
- ['metadata', 'annotations', 'firestartr.dev/dest-annotation'],
165775
- ],
165776
- };
165777
- const key = ref.join(',');
165778
- const alts = altTable[key];
165779
- if (alts) {
165780
- for (const alt of alts) {
165781
- const av = lodash_default().get(doc, alt);
165782
- if (av !== undefined)
165783
- return av;
165784
- }
165785
- }
165786
- // Reverse fallback: if ref is CR-shaped, try claim-shaped
165787
- const reverseTable = {
165788
- 'spec,org': ['providers', 'github', 'org'],
165789
- 'spec,provisioner,technology,stack': ['providers', 'github', 'org'],
165790
- 'spec,firestartr,technology,stack': ['providers', 'github', 'org'],
165791
- 'metadata,name': ['name'],
165792
- 'metadata,annotations,firestartr.dev/dest-annotation': [
165793
- 'annotations',
165794
- 'firestartr.dev/dest-annotation',
165795
- ],
165796
- 'spec,provisioner,org': ['providers', 'github', 'org'],
165797
- 'spec,provisioner,repo,defaultBranch': [
165798
- 'providers',
165799
- 'github',
165800
- 'branchStrategy',
165801
- 'defaultBranch',
165802
- ],
165803
- 'spec,repo,defaultBranch': [
165804
- 'providers',
165805
- 'github',
165806
- 'branchStrategy',
165807
- 'defaultBranch',
165808
- ],
165809
- 'metadata,annotations,firestartr.dev/external-name': [
165810
- 'providers',
165811
- 'github',
165812
- 'name',
165813
- ],
165814
- };
165815
- const rev = reverseTable[ref.join(',')];
165816
- if (rev) {
165817
- const rv = lodash_default().get(doc, rev);
165818
- if (rv !== undefined)
165819
- return rv;
165820
- }
165821
- return v;
165822
- };
165823
166514
  Object.entries(args).forEach(([key, arg]) => {
165824
166515
  if ('$default' in arg) {
165825
166516
  if (Array.isArray(arg['$default'])) {
165826
- context[key] = resolveRef(stitchedClaim, arg['$default']);
166517
+ context[key] = lodash_default().get(entity, arg['$default']);
165827
166518
  }
165828
166519
  else {
165829
166520
  context[key] = arg['$default'];
165830
166521
  }
165831
166522
  }
165832
166523
  if ('$ref' in arg) {
165833
- const refValue = resolveRef(stitchedClaim, arg['$ref']);
166524
+ const refValue = lodash_default().get(entity, arg['$ref']);
165834
166525
  context[key] = refValue;
165835
166526
  }
165836
166527
  else if ('$lit' in arg) {
@@ -165960,7 +166651,6 @@ function updateFileContent(featureRenderPath, filePath, content) {
165960
166651
 
165961
166652
 
165962
166653
 
165963
-
165964
166654
  /* harmony default export */ const features_renderer = ({
165965
166655
  validate: validate,
165966
166656
  render: render,
@@ -165970,7 +166660,6 @@ function updateFileContent(featureRenderPath, filePath, content) {
165970
166660
  renderContent: renderContent,
165971
166661
  expandFiles: expandFiles,
165972
166662
  validateFeatureArgs: validateFeatureArgs,
165973
- getPatches: getPatches,
165974
166663
  });
165975
166664
 
165976
166665
  ;// CONCATENATED MODULE: ../features_preparer/src/logger.ts
@@ -165995,13 +166684,6 @@ function renderFeature(featureName, version, owner, repo, featureOwner, renderPa
165995
166684
  function renderFeatureFromPath(extractPath, renderedPath, featureOwner, featureArgs = {}) {
165996
166685
  return features_renderer.render(extractPath, renderedPath, featureOwner, {}, featureArgs);
165997
166686
  }
165998
- function getFeaturePatches(featureName, version, owner, repo, featureOwner, featureArgs = {}) {
165999
- const extractPath = external_path_default().join(catalog_common/* default.features.tarballs.getFeaturesExtractPath */.Z.features.tarballs.getFeaturesExtractPath(featureName, version, owner, repo), 'packages', featureName);
166000
- return getPatches(extractPath, featureOwner, {}, featureArgs);
166001
- }
166002
- function getFeaturePatchesFromPath(extractPath, featureOwner, featureArgs = {}) {
166003
- return getPatches(extractPath, featureOwner, {}, featureArgs);
166004
- }
166005
166687
 
166006
166688
  // EXTERNAL MODULE: ../../node_modules/adm-zip/adm-zip.js
166007
166689
  var adm_zip = __nccwpck_require__(7993);
@@ -166060,27 +166742,56 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
166060
166742
  ref: reference,
166061
166743
  url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
166062
166744
  };
166063
- return renderFeature(featureName, reference, owner, repo, featureOwner, '/tmp', featureArgs);
166064
- }
166065
- async function installer_getFeaturePatches(featureName, version, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166066
- const reference = `${featureName}-v${version}`;
166067
- return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166068
- }
166069
- async function getFeaturePatchesFromRef(featureName, featureRef, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166070
- const reference = `${featureRef}`;
166071
- return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166072
- }
166073
- async function processFeaturePatches(featureName, reference, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166074
- await downloadFeatureZip(repo, featureName, reference, owner);
166745
+ const commitInfo = await resolveFeatureCommit(owner, repo, reference);
166075
166746
  featureArgs['traceability'] = {
166076
- version: reference,
166077
- owner,
166078
- repo,
166079
- name: featureName,
166080
- ref: reference,
166081
- url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
166747
+ ...featureArgs['traceability'],
166748
+ sha: commitInfo.sha,
166749
+ tags: commitInfo.tags,
166082
166750
  };
166083
- return getFeaturePatches(featureName, reference, owner, repo, featureOwner, featureArgs);
166751
+ return renderFeature(featureName, reference, owner, repo, featureOwner, '/tmp', featureArgs);
166752
+ }
166753
+ async function resolveFeatureCommit(owner, repo, reference) {
166754
+ try {
166755
+ const octokit = await github["default"].getOctokitForOrg(owner);
166756
+ // Get the resolved commit SHA
166757
+ const { data: commit } = await octokit.rest.repos.getCommit({
166758
+ owner,
166759
+ repo,
166760
+ ref: reference,
166761
+ });
166762
+ const sha = commit.sha;
166763
+ // Get all tags pointing to this commit.
166764
+ // Tag enumeration is wrapped separately so a pagination failure
166765
+ // still returns the successfully resolved SHA.
166766
+ const tags = [];
166767
+ try {
166768
+ let page = 1;
166769
+ while (true) {
166770
+ const { data: tagList } = await octokit.rest.repos.listTags({
166771
+ owner,
166772
+ repo,
166773
+ per_page: 100,
166774
+ page,
166775
+ });
166776
+ const matchingTags = tagList.filter((t) => t.commit?.sha === sha);
166777
+ tags.push(...matchingTags.map((t) => t.name));
166778
+ if (tagList.length < 100)
166779
+ break;
166780
+ page++;
166781
+ }
166782
+ }
166783
+ catch (tagError) {
166784
+ // Clear partial results so consumers don't publish an incomplete
166785
+ // tag set as if it were the full list.
166786
+ tags.length = 0;
166787
+ features_preparer_src_logger.warn(`[features_preparer] Failed to list tags for ${owner}/${repo}@${reference}: ${tagError instanceof Error ? tagError.message : String(tagError)}`);
166788
+ }
166789
+ return { sha, tags };
166790
+ }
166791
+ catch (error) {
166792
+ features_preparer_src_logger.warn(`[features_preparer] Failed to resolve commit for ${owner}/${repo}@${reference}: ${error instanceof Error ? error.message : String(error)}`);
166793
+ return { sha: '', tags: [] };
166794
+ }
166084
166795
  }
166085
166796
  async function prepareFeature(featureName, version, repo = 'features', owner = 'prefapp') {
166086
166797
  await downloadFeatureZip(repo, featureName, version, owner);
@@ -166136,9 +166847,6 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166136
166847
  /* harmony default export */ const src = ({
166137
166848
  renderFeature: renderFeature,
166138
166849
  renderFeatureFromPath: renderFeatureFromPath,
166139
- getFeaturePatches: installer_getFeaturePatches,
166140
- getFeaturePatchesFromRef: getFeaturePatchesFromRef,
166141
- getFeaturePatchesFromPath: getFeaturePatchesFromPath,
166142
166850
  getFeatureConfigFromRef: getFeatureConfigFromRef,
166143
166851
  getFeatureConfig: getFeatureConfig,
166144
166852
  prepareFeature: prepareFeature,
@@ -166155,1118 +166863,6 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166155
166863
  getFeatureConfigFromRef: src.getFeatureConfigFromRef,
166156
166864
  });
166157
166865
 
166158
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/protectedPaths.ts
166159
- // Platform-curated protected claim paths — features may not write to these.
166160
- // Stored as JSON Pointers (RFC 6901). Supports wildcard `*` for a single segment.
166161
- // Example: /providers/STAR/org matches /providers/github/org where STAR is *.
166162
- const PROTECTED_CLAIM_PATHS = [
166163
- '/kind',
166164
- '/name',
166165
- '/providers/*/org',
166166
- '/providers/github/org',
166167
- '/providers/github/name',
166168
- '/metadata/name',
166169
- '/metadata/namespace',
166170
- ];
166171
- function isProtectedPath(patchPath) {
166172
- const normalizedPatch = normalizePointer(patchPath);
166173
- for (const protectedPath of PROTECTED_CLAIM_PATHS) {
166174
- if (matchesProtected(protectedPath, normalizedPatch)) {
166175
- return true;
166176
- }
166177
- }
166178
- return false;
166179
- }
166180
- function normalizePointer(pointer) {
166181
- if (!pointer.startsWith('/')) {
166182
- return '/' + pointer;
166183
- }
166184
- return pointer;
166185
- }
166186
- function matchesProtected(protectedPointer, patchPointer) {
166187
- const protectedSegments = protectedPointer
166188
- .split('/')
166189
- .slice(1)
166190
- .map(unescapePointer);
166191
- const patchSegments = patchPointer.split('/').slice(1).map(unescapePointer);
166192
- // Treat ancestor/descendant as protected: a patch to a parent (e.g. /metadata)
166193
- // would replace the protected child (/metadata/name), and a patch to a child
166194
- // (e.g. /metadata/name/extra) is also under the protected tree. Match when
166195
- // the shorter prefix equals the longer prefix (with wildcard support).
166196
- const minLen = Math.min(protectedSegments.length, patchSegments.length);
166197
- for (let i = 0; i < minLen; i++) {
166198
- if (protectedSegments[i] === '*')
166199
- continue;
166200
- if (protectedSegments[i] !== patchSegments[i])
166201
- return false;
166202
- }
166203
- return true;
166204
- }
166205
- function unescapePointer(segment) {
166206
- return segment.replace(/~1/g, '/').replace(/~0/g, '~');
166207
- }
166208
-
166209
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/errors.ts
166210
- class StitchingError extends Error {
166211
- constructor(violations) {
166212
- const lines = violations.map((v) => ` - ${v.message}`).join('\n');
166213
- super(`Claim stitching failed with ${violations.length} violation(s):\n${lines}`);
166214
- this.name = 'StitchingError';
166215
- this.violations = violations;
166216
- }
166217
- }
166218
- function formatStitchingError(error) {
166219
- return error.message;
166220
- }
166221
-
166222
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/stitching.ts
166223
-
166224
-
166225
-
166226
-
166227
- let MOCK_FEATURES_FN;
166228
- function MOCK_STITCHING_FEATURES(mock) {
166229
- MOCK_FEATURES_FN = mock;
166230
- }
166231
- function pointerExists(doc, pointer) {
166232
- const segments = pointer
166233
- .split('/')
166234
- .slice(1)
166235
- .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
166236
- let cur = doc;
166237
- for (const seg of segments) {
166238
- if (cur === null || cur === undefined || typeof cur !== 'object')
166239
- return false;
166240
- if (!Object.prototype.hasOwnProperty.call(cur, seg))
166241
- return false;
166242
- cur = cur[seg];
166243
- }
166244
- return true;
166245
- }
166246
- function isAppendPointer(pointer) {
166247
- return pointer.endsWith('/-');
166248
- }
166249
- function isRecord(value) {
166250
- return typeof value === 'object' && value !== null && !Array.isArray(value);
166251
- }
166252
- // Deduplication identity for a feature patch. Ordinary writes collide on the
166253
- // normalized pointer. Array appends (`add ... /-`) collide on the pointer plus
166254
- // the appended element's `name` when present, so several features can each add
166255
- // a distinct label to /providers/github/labels/- while the same label name from
166256
- // two features is silently deduplicated (or errors if values differ).
166257
- function writeIdentity(patch) {
166258
- const path = normalizePointer(patch.path);
166259
- if (patch.op === 'add' &&
166260
- isAppendPointer(path) &&
166261
- isRecord(patch.value) &&
166262
- typeof patch.value.name === 'string') {
166263
- return `${path}#${patch.value.name}`;
166264
- }
166265
- return path;
166266
- }
166267
- // Check whether an array at `appendPath`'s parent already contains an element
166268
- // whose `name` matches `value.name`. Used for the "claim wins" gate: when a
166269
- // feature tries to append an array element that already exists in the claim,
166270
- // the patch is silently dropped.
166271
- function arrayElementExists(doc, appendPath, value) {
166272
- if (!isRecord(value) || typeof value.name !== 'string')
166273
- return false;
166274
- const parentPath = appendPath.slice(0, -2); // strip trailing /-
166275
- const segments = parentPath
166276
- .split('/')
166277
- .slice(1)
166278
- .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
166279
- let cur = doc;
166280
- for (const seg of segments) {
166281
- if (cur === null || cur === undefined || typeof cur !== 'object')
166282
- return false;
166283
- if (!Object.prototype.hasOwnProperty.call(cur, seg))
166284
- return false;
166285
- cur = cur[seg];
166286
- }
166287
- if (!Array.isArray(cur))
166288
- return false;
166289
- return cur.some((el) => isRecord(el) && el.name === value.name);
166290
- }
166291
- // fast-json-patch does not create intermediate containers for `add`, and
166292
- // appending to an array requires the array to already exist. Ensure every
166293
- // ancestor segment exists before applying an `add`: missing containers are
166294
- // created as objects, except the immediate parent of a `/-` append, which is
166295
- // created as an array. Existing (user/default-declared) values are never
166296
- // overwritten.
166297
- function ensureAddParents(doc, patch) {
166298
- const segments = patch.path.split('/').slice(1);
166299
- let prefix = '';
166300
- for (let i = 0; i < segments.length - 1; i++) {
166301
- prefix += '/' + segments[i];
166302
- if (pointerExists(doc, prefix))
166303
- continue;
166304
- const isArrayParent = isAppendPointer(patch.path) && i === segments.length - 2;
166305
- const value = isArrayParent ? [] : {};
166306
- try {
166307
- doc = fast_json_patch.applyPatch(doc, [{ op: 'add', path: prefix, value }], false, false).newDocument;
166308
- }
166309
- catch {
166310
- // Concurrent structural conflict — let the actual patch surface the error.
166311
- }
166312
- }
166313
- return doc;
166314
- }
166315
- async function stitchClaim(claimAfterDefaults, preCollectedEnvelopes) {
166316
- const envelopes = preCollectedEnvelopes ?? (await collectFeaturePatches(claimAfterDefaults));
166317
- if (envelopes.length === 0) {
166318
- return claimAfterDefaults;
166319
- }
166320
- // Normalize all patches once so every gate and the apply phase use the same pointers.
166321
- const normalizedEnvelopes = envelopes.map((env) => ({
166322
- feature: env.feature,
166323
- patches: env.patches.map((p) => ({
166324
- ...p,
166325
- path: normalizePointer(p.path),
166326
- _raw: p.path,
166327
- })),
166328
- }));
166329
- // ── Phase 1: silently drop patches that should not be applied ──────────
166330
- // • Array append whose element already exists in the claim → claim wins
166331
- // • Duplicate identity with identical value across features → silent dedup
166332
- const dropped = new Set(); // "envelopeIdx:patchIdx"
166333
- const seenIdentities = new Map();
166334
- for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166335
- const envelope = normalizedEnvelopes[ei];
166336
- for (let pi = 0; pi < envelope.patches.length; pi++) {
166337
- const patch = envelope.patches[pi];
166338
- const key = `${ei}:${pi}`;
166339
- // Array claim-wins: element already in claim → drop
166340
- if (patch.op === 'add' && isAppendPointer(patch.path)) {
166341
- if (arrayElementExists(claimAfterDefaults, patch.path, patch.value)) {
166342
- dropped.add(key);
166343
- continue;
166344
- }
166345
- }
166346
- // Silent dedup: same identity + same value → drop
166347
- const identity = writeIdentity(patch);
166348
- const existing = seenIdentities.get(identity);
166349
- if (existing) {
166350
- if (JSON.stringify(existing.value) === JSON.stringify(patch.value)) {
166351
- dropped.add(key);
166352
- continue;
166353
- }
166354
- // Different value — kept here, caught as a violation in Phase 2.
166355
- }
166356
- else {
166357
- seenIdentities.set(identity, {
166358
- feature: envelope.feature,
166359
- value: patch.value,
166360
- });
166361
- }
166362
- }
166363
- }
166364
- // ── Phase 2: collect violations on non-dropped patches ─────────────────
166365
- const violations = [];
166366
- const seenForViolations = new Map(); // identity → feature
166367
- for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166368
- const envelope = normalizedEnvelopes[ei];
166369
- for (let pi = 0; pi < envelope.patches.length; pi++) {
166370
- if (dropped.has(`${ei}:${pi}`))
166371
- continue;
166372
- const patch = envelope.patches[pi];
166373
- const path = patch.path;
166374
- const raw = patch._raw ?? path;
166375
- // Gate 1: protected path
166376
- if (isProtectedPath(path)) {
166377
- violations.push({
166378
- feature: envelope.feature,
166379
- op: patch.op,
166380
- path,
166381
- reason: 'protected-path',
166382
- message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets protected claim path`,
166383
- });
166384
- }
166385
- // Gate 2: conflicting value from another feature
166386
- const identity = writeIdentity(patch);
166387
- if (seenForViolations.has(identity)) {
166388
- violations.push({
166389
- feature: envelope.feature,
166390
- op: patch.op,
166391
- path,
166392
- reason: 'duplicate-path',
166393
- conflictingFeature: seenForViolations.get(identity),
166394
- message: `Feature '${envelope.feature}' and '${seenForViolations.get(identity)}' both write to ${path} (deduplication)`,
166395
- });
166396
- }
166397
- else {
166398
- seenForViolations.set(identity, envelope.feature);
166399
- }
166400
- // Gate 3: replace / remove to an absent path
166401
- if (['replace', 'remove'].includes(patch.op)) {
166402
- if (!pointerExists(claimAfterDefaults, path)) {
166403
- violations.push({
166404
- feature: envelope.feature,
166405
- op: patch.op,
166406
- path,
166407
- reason: 'overwrite-protected',
166408
- message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets absent path (replace/remove requires existing value)`,
166409
- });
166410
- }
166411
- }
166412
- }
166413
- }
166414
- if (violations.length > 0) {
166415
- throw new StitchingError(violations);
166416
- }
166417
- // ── Phase 3: apply remaining patches ───────────────────────────────────
166418
- let stitched = JSON.parse(JSON.stringify(claimAfterDefaults));
166419
- for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166420
- const envelope = normalizedEnvelopes[ei];
166421
- const patchesToApply = envelope.patches.filter((_, pi) => !dropped.has(`${ei}:${pi}`));
166422
- if (patchesToApply.length === 0)
166423
- continue;
166424
- for (const patch of patchesToApply) {
166425
- if (patch.op === 'add') {
166426
- stitched = ensureAddParents(stitched, patch);
166427
- }
166428
- }
166429
- const result = fast_json_patch.applyPatch(stitched, patchesToApply, false, false);
166430
- stitched = result.newDocument;
166431
- }
166432
- return stitched;
166433
- }
166434
- async function collectFeaturePatches(claim) {
166435
- if (MOCK_FEATURES_FN) {
166436
- return MOCK_FEATURES_FN(claim);
166437
- }
166438
- const typedClaim = claim;
166439
- const features = typedClaim.providers?.github?.features ?? [];
166440
- if (features.length === 0)
166441
- return [];
166442
- // In unit tests without GitHub credentials, avoid real download — tests that care
166443
- // about stitching should set MOCK_STITCHING_FEATURES explicitly.
166444
- if (process.env.JEST_WORKER_ID && !process.env.GITHUB_TOKEN) {
166445
- return [];
166446
- }
166447
- const envelopes = [];
166448
- for (const feature of features) {
166449
- const featureName = feature.name;
166450
- const versionOrRef = feature.ref ?? feature.version ?? '';
166451
- const repo = feature.repo
166452
- ? (feature.repo.split('/')[1] ?? 'features')
166453
- : 'features';
166454
- const owner = feature.repo
166455
- ? (feature.repo.split('/')[0] ?? 'prefapp')
166456
- : 'prefapp';
166457
- const args = feature.args ?? {};
166458
- try {
166459
- let patchesOrRendered;
166460
- const preparerAny = features_preparer;
166461
- if (feature.ref) {
166462
- if (typeof preparerAny.getFeaturePatchesFromRef === 'function') {
166463
- patchesOrRendered = await preparerAny.getFeaturePatchesFromRef(featureName, versionOrRef, claim, args, repo, owner);
166464
- }
166465
- else {
166466
- patchesOrRendered = await preparerAny.getFeatureConfigFromRef(featureName, versionOrRef, claim, args, repo, owner);
166467
- }
166468
- }
166469
- else {
166470
- if (typeof preparerAny.getFeaturePatches === 'function') {
166471
- patchesOrRendered = await preparerAny.getFeaturePatches(featureName, versionOrRef, claim, args, repo, owner);
166472
- }
166473
- else {
166474
- patchesOrRendered = await preparerAny.getFeatureConfig(featureName, versionOrRef, claim, args, repo, owner);
166475
- }
166476
- }
166477
- const patches = Array.isArray(patchesOrRendered)
166478
- ? patchesOrRendered
166479
- : extractClaimPatches(patchesOrRendered);
166480
- envelopes.push({ feature: featureName, patches });
166481
- }
166482
- catch (err) {
166483
- throw new Error(`Failed to collect patches for feature '${featureName}': ${err instanceof Error ? err.message : String(err)}`);
166484
- }
166485
- }
166486
- return envelopes;
166487
- }
166488
- function extractClaimPatches(rendered) {
166489
- const patches = rendered.patches;
166490
- if (!patches)
166491
- return [];
166492
- if (Array.isArray(patches))
166493
- return patches;
166494
- throw new Error(`Feature patches must be a flat array (claim-relative pointers like /annotations/...), got ${typeof patches}: ${JSON.stringify(patches).slice(0, 200)}. See #2648`);
166495
- }
166496
-
166497
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
166498
-
166499
-
166500
-
166501
-
166502
-
166503
-
166504
-
166505
-
166506
-
166507
-
166508
-
166509
-
166510
-
166511
-
166512
-
166513
-
166514
-
166515
- // We need to ensure the grep command is a gnu version
166516
- let IS_GNU_GREP = false;
166517
- let GREP_ON_CHECK = false;
166518
- // this loop is for avoid race conditions
166519
- // once a check is running no more checks
166520
- async function waitForCheck() {
166521
- const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
166522
- do {
166523
- await f_wait();
166524
- } while (GREP_ON_CHECK);
166525
- }
166526
- function checkGrep() {
166527
- return new Promise((ok, ko) => {
166528
- // idempotency: already checked
166529
- if (IS_GNU_GREP)
166530
- return ok();
166531
- // a check is already running no new checks
166532
- if (GREP_ON_CHECK)
166533
- return waitForCheck();
166534
- logger.info('Checking the grep command');
166535
- GREP_ON_CHECK = true;
166536
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
166537
- handler.on('close', (code) => {
166538
- GREP_ON_CHECK = false;
166539
- if (code === 0) {
166540
- logger.debug('Grep is a gnu grep');
166541
- IS_GNU_GREP = true;
166542
- ok();
166543
- }
166544
- else {
166545
- ko('Lazy evaluation needs a gnu-grep command');
166546
- }
166547
- });
166548
- });
166549
- }
166550
- async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
166551
- await checkGrep();
166552
- let result = existingRefs;
166553
- logger.info(`Load reference ${claimRef}`);
166554
- initVirtualClaims(org);
166555
- logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
166556
- // cargas datos con grep
166557
- try {
166558
- const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
166559
- let claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
166560
- // Claim stitching: feature claim-patches with three gates before AJV
166561
- try {
166562
- claim = await stitchClaim(claim);
166563
- }
166564
- catch (e) {
166565
- throw new Error(`Error when stitching claim ${claimRef}: ${e instanceof Error ? e.message : String(e)}`);
166566
- }
166567
- let variants = [];
166568
- if (claim.kind === 'TFWorkspaceClaim' &&
166569
- claim.providers?.terraform?.variants) {
166570
- variants = claim.providers.terraform.variants;
166571
- delete claim.providers.terraform.variants;
166572
- }
166573
- logger.silly(`Patched claim is:
166574
- ---
166575
- ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
166576
- try {
166577
- validateClaim(claim, base[`${claim.kind}Schema`]);
166578
- await optionalValidation(claim);
166579
- }
166580
- catch (error) {
166581
- let errorMsg = '';
166582
- if (Array.isArray(error)) {
166583
- for (const data of error) {
166584
- errorMsg = `${errorMsg}
166585
- - ${data.instancePath}: ${data.message}`;
166586
- }
166587
- }
166588
- else {
166589
- errorMsg = error instanceof Error ? error.message : String(error);
166590
- }
166591
- throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
166592
- }
166593
- result[claimRef] = {};
166594
- result[claimRef]['claim'] = claim;
166595
- result[claimRef]['claimPath'] = VisitedClaims[claimRef];
166596
- if (VisitedClaims[claimRef] === 'virtual') {
166597
- result = await setVirtualClaimAdditionalData(result, claim, claimRef);
166598
- }
166599
- else {
166600
- result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166601
- }
166602
- const claimKind = claim.kind;
166603
- const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
166604
- for (const ref of references) {
166605
- if (!result[ref]) {
166606
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166607
- result = lodash_default().merge(result, resolvedReferences);
166608
- }
166609
- }
166610
- if (variants.length > 0) {
166611
- const parentClaimPath = VisitedClaims[claimRef];
166612
- for (const variant of variants) {
166613
- if (!variant.name || !variant.overrides) {
166614
- throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
166615
- }
166616
- const prohibitedOverrideFields = ['source', 'module', 'name'];
166617
- for (const field of prohibitedOverrideFields) {
166618
- if (field in variant.overrides) {
166619
- throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
166620
- }
166621
- }
166622
- const composedName = claim.providers.terraform.name + '-' + variant.name;
166623
- const variantClaim = lodash_default().cloneDeep(claim);
166624
- variantClaim._parentClaimName = claim.name;
166625
- variantClaim.name = composedName;
166626
- variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
166627
- variantClaim.providers.terraform.name = composedName;
166628
- if (!variantClaim.annotations) {
166629
- variantClaim.annotations = {};
166630
- }
166631
- variantClaim.annotations['firestartr.dev/variant-of'] =
166632
- claim.providers.terraform.name;
166633
- const variantRef = `TFWorkspaceClaim-${composedName}`;
166634
- if (result[variantRef]) {
166635
- throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
166636
- }
166637
- logger.info(`Creating synthetic variant claim ${variantRef}`);
166638
- result[variantRef] = {};
166639
- result[variantRef]['claim'] = variantClaim;
166640
- result[variantRef]['claimPath'] = parentClaimPath;
166641
- result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166642
- const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
166643
- for (const ref of variantReferences) {
166644
- if (!result[ref]) {
166645
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166646
- result = lodash_default().merge(result, resolvedReferences);
166647
- }
166648
- }
166649
- }
166650
- }
166651
- }
166652
- catch (err) {
166653
- throw `Lazy Loading: ${err}`;
166654
- }
166655
- return [result, postValidations];
166656
- }
166657
- let LoadedClaims = {};
166658
- let VisitedClaims = {};
166659
- let DuplicatedClaims = {};
166660
- function resetLazyLoader() {
166661
- LoadedClaims = {};
166662
- VisitedClaims = {};
166663
- DuplicatedClaims = {};
166664
- }
166665
- async function lazyGetClaim(kind, name, org, cwd) {
166666
- const indice = `${kind}-${name}`;
166667
- logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
166668
- if (indice in LoadedClaims)
166669
- return LoadedClaims[indice];
166670
- if (isVirtualClaim(kind, name)) {
166671
- await loadVirtualClaim(kind, name, org);
166672
- }
166673
- else {
166674
- await getClaimsByName(name, cwd);
166675
- }
166676
- if (indice in LoadedClaims) {
166677
- return LoadedClaims[indice];
166678
- }
166679
- else {
166680
- throw new Error(`Error: ${kind}-${name} not found`);
166681
- }
166682
- }
166683
- async function getClaimsByName(name, cwd = '.') {
166684
- return new Promise((ok, ko) => {
166685
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
166686
- cwd: cwd,
166687
- });
166688
- logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
166689
- const entradas = [];
166690
- const chunks = [];
166691
- handler.stdout.on('data', (data) => {
166692
- chunks.push(data.toString('utf-8'));
166693
- });
166694
- handler.on('close', () => {
166695
- const dataFinal = chunks.join('');
166696
- const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
166697
- for (const fileName of fileNameList) {
166698
- // only yaml files
166699
- if (!fileName.match(/\.yml$|\.yaml$/)) {
166700
- continue;
166701
- }
166702
- // only entries not already visited
166703
- if (Object.values(VisitedClaims).includes(fileName)) {
166704
- continue;
166705
- }
166706
- entradas.push(external_path_.join(cwd, fileName));
166707
- }
166708
- return Promise.all(entradas.map((entrada) => {
166709
- return loadRawClaim(entrada);
166710
- }))
166711
- .then(() => {
166712
- ok();
166713
- })
166714
- .catch((err) => {
166715
- ko(err);
166716
- });
166717
- });
166718
- });
166719
- }
166720
- async function loadRawClaim(entry) {
166721
- return new Promise((ok, ko) => {
166722
- external_fs_.readFile(entry, 'utf-8', (error, data) => {
166723
- if (error)
166724
- return ko(`Reading ${entry}: ${error}`);
166725
- const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166726
- if (!('kind' in claim && 'name' in claim)) {
166727
- logger.warn(`Invalid claim file ${entry}`);
166728
- }
166729
- else {
166730
- const claimKey = `${claim.kind}-${claim.name}`;
166731
- if (Object.keys(DuplicatedClaims).includes(claimKey) &&
166732
- DuplicatedClaims[claimKey] !== entry) {
166733
- return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
166734
- }
166735
- LoadedClaims[claimKey] = data;
166736
- VisitedClaims[claimKey] = entry;
166737
- DuplicatedClaims[claimKey] = entry;
166738
- ok();
166739
- }
166740
- });
166741
- });
166742
- }
166743
- async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
166744
- const virtualClaim = getVirtualClaim(claim.kind, claim.name);
166745
- renderedData[claimRef]['initializers'] = [
166746
- new UUIDInitializer(claim),
166747
- new InitializerClaimRef(),
166748
- new InitializerDefault(virtualClaim.getDefaultConfig()),
166749
- ];
166750
- renderedData[claimRef]['globals'] = [];
166751
- renderedData[claimRef]['overrides'] = [];
166752
- renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
166753
- return renderedData;
166754
- }
166755
- async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
166756
- renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
166757
- renderedData[claimRef]['globals'] = await loadGlobals(claim);
166758
- renderedData[claimRef]['overrides'] = loadOverrides(claim);
166759
- renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
166760
- return renderedData;
166761
- }
166762
- async function loadVirtualClaim(kind, name, org) {
166763
- const virtualClaim = getVirtualClaim(kind, name);
166764
- const expandedClaim = await virtualClaim.expand({ org });
166765
- logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
166766
- LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
166767
- VisitedClaims[`${kind}-${name}`] = 'virtual';
166768
- }
166769
-
166770
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
166771
-
166772
-
166773
-
166774
- /*
166775
- * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
166776
- * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
166777
- * - If the block is missing at the claim-level, the default block is applied
166778
- */
166779
- const defaultBlocksPaths = ['/providers/terraform/sync'];
166780
- function applyBlockAwareDefaults(claim, defaultClaim) {
166781
- // we always work with a clone of the original
166782
- const claimClone = JSON.parse(JSON.stringify(claim));
166783
- // if the block is claim-level defined we store it here
166784
- const defaultBlocks = new Map();
166785
- // we remove the blocks entirely of the claim
166786
- // to achieve an add copy
166787
- for (const defaultBlockPath of defaultBlocksPaths) {
166788
- if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
166789
- continue;
166790
- const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
166791
- if (originalValue) {
166792
- logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
166793
- defaultBlocks.set(defaultBlockPath, originalValue);
166794
- fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
166795
- }
166796
- }
166797
- // if the patch has the same path of a block
166798
- // and the block was defined at claim-level
166799
- // we interchange the value with the original value
166800
- // otherwise we use the default
166801
- const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
166802
- .filter((jp) => jp.op === 'add')
166803
- .map((jp) => {
166804
- if (defaultBlocks.has(jp.path)) {
166805
- return {
166806
- ...jp,
166807
- value: defaultBlocks.get(jp.path),
166808
- };
166809
- }
166810
- else {
166811
- return jp;
166812
- }
166813
- });
166814
- return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
166815
- }
166816
- function claimsDefaulter_hasDeepPath(data, deepPath) {
166817
- return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
166818
- }
166819
-
166820
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
166821
-
166822
-
166823
-
166824
-
166825
-
166826
-
166827
-
166828
-
166829
-
166830
-
166831
-
166832
-
166833
-
166834
-
166835
-
166836
- const isYamlFile = new RegExp(/\.(yaml|yml)$/);
166837
- /**
166838
- *
166839
- * @returns Virtual Claims
166840
- */
166841
- const virtualClaims = [new FirestartrAllClaim()];
166842
- /*
166843
- * Loads all applicable globals for a given claim and stores them into a list.
166844
- *
166845
- * Input:
166846
- * - claim: our custom claim object, with which we check if the global is applicable
166847
- *
166848
- * Returns:
166849
- * - A GlobalSection array promise, containing all the globals loaded for this claim
166850
- *
166851
- */
166852
- async function loadGlobals(claim) {
166853
- const result = [];
166854
- await crawl(getPath('globals'), (entry) => {
166855
- return isYamlFile.test(entry);
166856
- }, async (entry, data) => {
166857
- const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166858
- const ext = external_path_.extname(entry);
166859
- const name = external_path_.basename(entry, ext);
166860
- if (name in SECTIONS_BY_FILE_NAME) {
166861
- const section = new SECTIONS_BY_FILE_NAME[name](globalData);
166862
- if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
166863
- throw new Error(`Invalid ${name} global file`);
166864
- }
166865
- if (globalData.kind === claim.kind) {
166866
- result.push(section);
166867
- }
166868
- else {
166869
- logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
166870
- }
166871
- }
166872
- });
166873
- logger.info(`Loaded globals ${result}`);
166874
- return result;
166875
- }
166876
- /*
166877
- * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
166878
- * overrides and stores them into a list. Otherwise, it returns an empty list.
166879
- *
166880
- * Input:
166881
- * - claim: our custom claim object, which should be a GithubRepositoryClaim
166882
- *
166883
- * Returns:
166884
- * - An Overrider array, with a GithubRepositoryOverrider if the claim
166885
- * was a GithubRepositoryClaim, or empty if it was not.
166886
- *
166887
- */
166888
- function loadOverrides(claim) {
166889
- const overrides = [];
166890
- if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
166891
- overrides.push(new GithubRepositoryOverrider());
166892
- }
166893
- return overrides;
166894
- }
166895
- /**
166896
- * Load all normalizers and stores them into a list.
166897
- * @param claim
166898
- * @returns
166899
- */
166900
- async function loadNormalizers(_claim, path) {
166901
- const result = [];
166902
- for (const normalizer of NORMALIZERS) {
166903
- result.push(new normalizer({ path: path }));
166904
- }
166905
- return result;
166906
- }
166907
- /*
166908
- * Loads all applicable initializers for a given claim and stores them into a list.
166909
- *
166910
- * Input:
166911
- * - claim: our custom claim object, with which we check if the initializer is applicable
166912
- *
166913
- * Returns:
166914
- * - An Initializer array promise, containing all the initializers loaded for this claim
166915
- *
166916
- */
166917
- let loadedInitializers = false;
166918
- async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
166919
- const result = [];
166920
- await crawl(initializersPath, (entry) => {
166921
- return isYamlFile.test(entry);
166922
- }, async (entry, data) => {
166923
- const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166924
- const ext = external_path_.extname(entry);
166925
- const name = external_path_.basename(entry, ext);
166926
- if (name in INITIALIZERS_BY_FILE_NAME) {
166927
- const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
166928
- if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
166929
- throw new Error(`Invalid ${name} initializer file`);
166930
- }
166931
- if (initializerData.kind === claim.kind) {
166932
- result.push(initializerBfN);
166933
- }
166934
- else {
166935
- logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
166936
- }
166937
- }
166938
- });
166939
- for (const initializer of INITIALIZERS) {
166940
- if (initializer.applicableKinds.includes(claim.kind)) {
166941
- result.push(new initializer({ path: claimPath }));
166942
- }
166943
- }
166944
- logger.info(`Loaded initializers ${JSON.stringify(result)}`);
166945
- loadedInitializers = result;
166946
- return loadedInitializers;
166947
- }
166948
- function loadClaimDefaults() {
166949
- try {
166950
- return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
166951
- }
166952
- catch (error) {
166953
- const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
166954
- logger.error(errMsg);
166955
- throw new Error(errMsg);
166956
- }
166957
- }
166958
- /*
166959
- * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
166960
- *
166961
- * Input:
166962
- * - claim: our custom claim object, which we want to patch
166963
- * - defaultsClaims: an object, containing the defaults for each claim kind,
166964
- * with the same structure that we use for the actual claims
166965
- *
166966
- * Returns:
166967
- * - The patched claim
166968
- *
166969
- */
166970
- function loader_patchClaim(claim, defaultsClaims) {
166971
- if (defaultsClaims[claim.kind]) {
166972
- claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
166973
- }
166974
- return claim;
166975
- }
166976
- async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
166977
- const result = {};
166978
- const fullResult = {};
166979
- await crawlWithExclusions(crsPath, (entry) => {
166980
- return isYamlFile.test(entry);
166981
- }, async (entry, data) => {
166982
- const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
166983
- const metadata = yamlData?.metadata;
166984
- if (!(yamlData &&
166985
- typeof yamlData === 'object' &&
166986
- 'kind' in yamlData &&
166987
- metadata &&
166988
- typeof metadata === 'object' &&
166989
- 'name' in metadata)) {
166990
- logger.warn(`Invalid CR file ${entry}`);
166991
- }
166992
- else {
166993
- const crKey = `${yamlData.kind}-${metadata.name}`;
166994
- if (fullResult[crKey]) {
166995
- logger.error(`Duplicate CR ${crKey}`);
166996
- }
166997
- // Keep the complete CR set for validations that need repository-wide context.
166998
- fullResult[crKey] = yamlData;
166999
- // Keep the filtered CR set for previous-CR lookup during claim rendering.
167000
- if (allowedClaimReferences.length === 0 ||
167001
- allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
167002
- result[crKey] = yamlData;
167003
- }
167004
- }
167005
- }, excludedPaths.concat(getAdditionalPaths()));
167006
- return { crs: result, fullCrs: fullResult };
167007
- }
167008
- async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
167009
- const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
167010
- return crs;
167011
- }
167012
- /*
167013
- * Description: Get the renamed using claimRef annotation
167014
- * Input: claims: any, crs: any
167015
- * Output: renames: IRenameResult[]
167016
- */
167017
- async function loadRenames(claims, crs) {
167018
- /*
167019
- * If checkRenames is false, we don't need to check for renames
167020
- */
167021
- const renamesEnabled = getRenamesEnabled();
167022
- if (!renamesEnabled)
167023
- return [];
167024
- const result = [];
167025
- /**
167026
- * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
167027
- */
167028
- for (const cr of Object.values(crs)) {
167029
- const anyCr = cr;
167030
- if (!anyCr?.metadata?.annotations)
167031
- continue;
167032
- // if the annotation is a claimRef
167033
- const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
167034
- const annotations = anyCr?.metadata?.annotations || {};
167035
- if (annotationName in annotations) {
167036
- const claimRef = anyCr.metadata.annotations[annotationName];
167037
- // split the annotation into kind and name
167038
- const [kind, name] = claimRef.split('/');
167039
- // if the claim exists, check if the name is different for the given provider
167040
- const referencedClaim = claims[`${kind}-${name}`].claim;
167041
- const oldName = anyCr.metadata.name;
167042
- const newName = referencedClaim.providers[getConfiguredProvider()].name;
167043
- if (oldName !== newName) {
167044
- result.push({
167045
- kind: referencedClaim.kind,
167046
- claimPath: claims[`${kind}-${name}`].claimPath,
167047
- claimName: referencedClaim.name,
167048
- oldName,
167049
- newName,
167050
- });
167051
- }
167052
- }
167053
- }
167054
- return result;
167055
- }
167056
- async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
167057
- const data = {
167058
- renderClaims: {},
167059
- crs: {},
167060
- fullCrs: {},
167061
- };
167062
- const defaults = loadClaimDefaults();
167063
- for await (const claimRef of claimRefList) {
167064
- const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
167065
- data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
167066
- }
167067
- const crClaimReferences = [];
167068
- for (const ref of Object.keys(data.renderClaims)) {
167069
- // Replaces only the first instance of '-'
167070
- crClaimReferences.push(ref.replace('-', '/'));
167071
- }
167072
- const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
167073
- data['crs'] = loadedCrs.crs;
167074
- data['fullCrs'] = loadedCrs.fullCrs;
167075
- return data;
167076
- }
167077
- function getOrg() {
167078
- const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
167079
- return groupOrg;
167080
- }
167081
-
167082
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
167083
- function validateTfStateKeyUniqueness(crs) {
167084
- const tfStateKeys = {};
167085
- for (const crKey of Object.keys(crs)) {
167086
- const cr = crs[crKey];
167087
- if (!isTerraformWorkspace(cr))
167088
- continue;
167089
- const tfKey = cr.spec.firestartr.tfStateKey;
167090
- if (tfStateKeys[tfKey]) {
167091
- throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
167092
-
167093
- The tfStateKey ${tfKey} is not unique,
167094
-
167095
- It is being used by the following CRs:
167096
-
167097
- - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
167098
-
167099
- - ${cr.kind} ${cr.metadata.name}
167100
-
167101
- Check the following Claims to ensure that the tfStateKey is unique:
167102
-
167103
- - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
167104
-
167105
- - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
167106
- `);
167107
- }
167108
- tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
167109
- }
167110
- }
167111
- function references_isCatalogEntity(cr) {
167112
- return [
167113
- 'Component',
167114
- 'Domain',
167115
- 'System',
167116
- 'User',
167117
- 'Group',
167118
- 'Resource',
167119
- ].includes(cr.kind);
167120
- }
167121
- function isTerraformWorkspace(cr) {
167122
- return cr.kind === 'FirestartrTerraformWorkspace';
167123
- }
167124
-
167125
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
167126
-
167127
- const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
167128
- function validateCrSizes(crs) {
167129
- for (const key of Object.keys(crs)) {
167130
- const cr = crs[key];
167131
- const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
167132
- const size = Buffer.byteLength(serialized, 'utf8');
167133
- if (size > K8S_OBJECT_SIZE_LIMIT) {
167134
- throw new Error(`CR "${cr.kind}-${cr.metadata.name}" exceeds the Kubernetes object size limit by ${size - K8S_OBJECT_SIZE_LIMIT} bytes. Maximum allowed is ${K8S_OBJECT_SIZE_LIMIT} bytes (1.5MiB).`);
167135
- }
167136
- }
167137
- }
167138
-
167139
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
167140
-
167141
- function validatePermissionsUniqueness(crs) {
167142
- for (const crkey in crs) {
167143
- const cr = crs[crkey];
167144
- if (cr.kind === 'FirestartrGithubRepository') {
167145
- const permissions = cr.spec.permissions;
167146
- logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
167147
- if (permissions) {
167148
- const rolesByIdentifier = new Map();
167149
- for (const perm of permissions) {
167150
- const identifier = perm.collaborator
167151
- ? perm.collaborator
167152
- : `ref:${perm.ref.kind}:${perm.ref.name}`;
167153
- const role = perm.role;
167154
- const prevRole = rolesByIdentifier.get(identifier);
167155
- if (prevRole === undefined) {
167156
- rolesByIdentifier.set(identifier, role);
167157
- }
167158
- else if (prevRole !== role) {
167159
- throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
167160
- }
167161
- }
167162
- }
167163
- }
167164
- }
167165
- }
167166
-
167167
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
167168
- const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
167169
- const CATALOG_KINDS = new Set([
167170
- 'User',
167171
- 'Group',
167172
- 'Component',
167173
- 'System',
167174
- 'Domain',
167175
- 'API',
167176
- 'Resource',
167177
- 'CatalogKubernetesResource',
167178
- 'CatalogTerraformWorkspace',
167179
- 'CatalogArgoDeploy',
167180
- 'CatalogOrgWebhook',
167181
- 'CatalogSecrets',
167182
- ]);
167183
- function validateNoBackstageAnnotationsInK8sCrs(crs) {
167184
- for (const crKey in crs) {
167185
- const cr = crs[crKey];
167186
- if (CATALOG_KINDS.has(cr.kind))
167187
- continue;
167188
- if (cr.metadata?.annotations) {
167189
- const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
167190
- if (backstageKeys.length > 0) {
167191
- throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
167192
- `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
167193
- }
167194
- }
167195
- }
167196
- }
167197
-
167198
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
167199
-
167200
- /**
167201
- * @param renderClaims renderClaims to sort
167202
- * @param kinds kinds to get from renderClaims
167203
- * @returns renderClaims with only the kinds specified
167204
- */
167205
- function sortRenderClaimsByKind(renderClaims, kinds) {
167206
- let result = {};
167207
- for (const kind of kinds) {
167208
- for (const key in renderClaims) {
167209
- if (renderClaims[key].claim.kind === kind) {
167210
- result[key] = renderClaims[key];
167211
- }
167212
- }
167213
- result = sortSelfKindRefs(result, kind);
167214
- }
167215
- return result;
167216
- }
167217
- function sortSelfKindRefs(renderClaims, kind) {
167218
- if (!hasSelfRefs(kind)) {
167219
- return renderClaims;
167220
- }
167221
- const newRenderClaims = {};
167222
- const data = extractRefs(renderClaims, kind);
167223
- const ordered = [];
167224
- const visiting = [];
167225
- const visited = [];
167226
- for (const key of Object.keys(data)) {
167227
- if (!visited.find((obj) => obj.name === key)) {
167228
- sortRefs(data, data[key], ordered, visiting, visited);
167229
- }
167230
- }
167231
- for (const cr of ordered) {
167232
- newRenderClaims[cr.name] = renderClaims[cr.name];
167233
- }
167234
- return newRenderClaims;
167235
- }
167236
- function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
167237
- if (visiting.find((obj) => obj.name === claim.name)) {
167238
- throw new Error(`Circular dependency detected for '${claim.name}'`);
167239
- }
167240
- if (!visited.find((obj) => obj.name === claim.name)) {
167241
- visiting.push(claim);
167242
- if (claim.refs) {
167243
- for (const ref of claim.refs) {
167244
- const referencedObject = data[ref];
167245
- if (!referencedObject) {
167246
- throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
167247
- }
167248
- sortRefs(data, referencedObject, ordered, visiting, visited);
167249
- }
167250
- }
167251
- visited.push(visiting.pop());
167252
- ordered.push(claim);
167253
- }
167254
- }
167255
- function hasSelfRefs(kind) {
167256
- return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
167257
- }
167258
-
167259
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
167260
-
167261
- function sortKinds(orderedKinds, claims) {
167262
- const result = [];
167263
- for (const kind of orderedKinds) {
167264
- const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
167265
- result.push(kindRenderClaims);
167266
- }
167267
- return result;
167268
- }
167269
-
167270
166866
  ;// CONCATENATED MODULE: ../cdk8s_renderer/src/overriders/utils.ts
167271
166867
 
167272
166868
 
@@ -167282,11 +166878,20 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
167282
166878
  files.push({ path, userManaged, content, targetBranch });
167283
166879
  }
167284
166880
  const featName = catalog_common/* default.generic.normalizeName */.Z.generic.normalizeName(`${featureName}-${cr.spec.firestartr.tfStateKey}`);
166881
+ const traceability = featureOutputObject.traceability || {};
167285
166882
  return {
167286
166883
  kind: 'GithubRepositoryFeatureClaim',
167287
166884
  version: '1.0.0',
167288
166885
  name: featName,
167289
- feature: { name: featureName, version: featureVersion },
166886
+ feature: {
166887
+ name: featureName,
166888
+ version: featureVersion,
166889
+ ref: traceability.ref,
166890
+ repo: traceability.repo,
166891
+ sha: traceability.sha,
166892
+ tags: traceability.tags,
166893
+ url: traceability.url,
166894
+ },
167290
166895
  firestartr: { tfStateKey: cr.spec.firestartr.tfStateKey },
167291
166896
  files,
167292
166897
  claimRepoName,
@@ -167308,12 +166913,14 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
167308
166913
 
167309
166914
 
167310
166915
 
166916
+
166917
+
167311
166918
  // this is the only way currently to make
167312
166919
  // work the system and inject our code
167313
166920
  // without having to download a feature
167314
- let featureOverride_MOCK_FEATURES_FN;
166921
+ let MOCK_FEATURES_FN;
167315
166922
  function MOCK_FEATURES(mock) {
167316
- featureOverride_MOCK_FEATURES_FN = mock;
166923
+ MOCK_FEATURES_FN = mock;
167317
166924
  if (mock) {
167318
166925
  log.warn('⚠️ Warning features system downloader has been mocked, it should only happen in unit testing');
167319
166926
  }
@@ -167344,8 +166951,8 @@ class FeaturesOverrider extends OverriderPatches {
167344
166951
  let owner = 'prefapp';
167345
166952
  let versionOrRef = this.featureVersion;
167346
166953
  let renderer = 'getFeatureConfig';
167347
- if (featureOverride_MOCK_FEATURES_FN) {
167348
- return featureOverride_MOCK_FEATURES_FN();
166954
+ if (MOCK_FEATURES_FN) {
166955
+ return MOCK_FEATURES_FN();
167349
166956
  }
167350
166957
  if (this.featureRepo) {
167351
166958
  [owner, repo] = this.featureRepo.split(/\//);
@@ -167363,8 +166970,8 @@ class FeaturesOverrider extends OverriderPatches {
167363
166970
  const featureName = this.featureName;
167364
166971
  const featureVersion = this.featureVersion || this.featureRef;
167365
166972
  const featureArgs = this.featureArgs;
167366
- const fSetUpAndRunRenderer = function (target) {
167367
- return this.setUpAndRunRenderer(target);
166973
+ const fSetUpAndRunRenderer = function (cr) {
166974
+ return this.setUpAndRunRenderer(cr);
167368
166975
  }.bind(this);
167369
166976
  let renderedFeatureConfig = null;
167370
166977
  return [
@@ -167373,7 +166980,7 @@ class FeaturesOverrider extends OverriderPatches {
167373
166980
  return true;
167374
166981
  },
167375
166982
  async apply(cr) {
167376
- renderedFeatureConfig = await fSetUpAndRunRenderer(claim);
166983
+ renderedFeatureConfig = await fSetUpAndRunRenderer(cr);
167377
166984
  return cr;
167378
166985
  },
167379
166986
  identify() {
@@ -167386,6 +166993,28 @@ class FeaturesOverrider extends OverriderPatches {
167386
166993
  return utils_renderFeature(featureName, featureVersion, renderedFeatureConfig, cr, claim.name);
167387
166994
  },
167388
166995
  },
166996
+ {
166997
+ isPostPatch: true,
166998
+ validate() {
166999
+ return true;
167000
+ },
167001
+ async apply(cr) {
167002
+ if (!renderedFeatureConfig) {
167003
+ logger.warn(`Feature ${featureName}: renderedFeatureConfig is null, skipping post-patch`);
167004
+ return cr;
167005
+ }
167006
+ const provider = helperCTX(this).provider;
167007
+ const jsonPatches = renderedFeatureConfig.patches?.[provider] || [];
167008
+ fast_json_patch_default().applyPatch(cr, jsonPatches);
167009
+ return cr;
167010
+ },
167011
+ identify() {
167012
+ return `feature-json-patch/${featureName}`;
167013
+ },
167014
+ applicable() {
167015
+ return { applicableProviders: ['catalog', 'github'] };
167016
+ },
167017
+ },
167389
167018
  ];
167390
167019
  }
167391
167020
  }
@@ -171135,6 +170764,23 @@ class FeatureRepoChart extends BaseGithubChart {
171135
170764
  annotations: {
171136
170765
  'firestartr.dev/external-name': claim.name,
171137
170766
  'firestartr.dev/feature-name': claim.feature.name,
170767
+ ...(claim.feature.sha
170768
+ ? { 'firestartr.dev/feature-git-sha': claim.feature.sha }
170769
+ : {}),
170770
+ ...(claim.feature.tags && claim.feature.tags.length > 0
170771
+ ? {
170772
+ 'firestartr.dev/feature-git-tags': JSON.stringify(claim.feature.tags),
170773
+ }
170774
+ : {}),
170775
+ ...(claim.feature.url
170776
+ ? { 'firestartr.dev/feature-url': claim.feature.url }
170777
+ : {}),
170778
+ ...(claim.feature.ref
170779
+ ? { 'firestartr.dev/feature-ref': claim.feature.ref }
170780
+ : {}),
170781
+ ...(claim.feature.repo
170782
+ ? { 'firestartr.dev/feature-repo': claim.feature.repo }
170783
+ : {}),
171138
170784
  },
171139
170785
  },
171140
170786
  spec: {
@@ -363740,7 +363386,7 @@ function helperCreateCheckRunName(cmd, item) {
363740
363386
 
363741
363387
  ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
363742
363388
  const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
363743
- function utils_isRecord(value) {
363389
+ function isRecord(value) {
363744
363390
  return typeof value === 'object' && value !== null;
363745
363391
  }
363746
363392
  function getFirstNonEmptyString(...values) {
@@ -363778,7 +363424,7 @@ function replaceInlineSecrets(inline, secrets) {
363778
363424
  return result;
363779
363425
  }
363780
363426
  function extractErrorDetails(error) {
363781
- const exitCode = utils_isRecord(error) && typeof error.exitCode === 'number'
363427
+ const exitCode = isRecord(error) && typeof error.exitCode === 'number'
363782
363428
  ? error.exitCode
363783
363429
  : undefined;
363784
363430
  if (typeof error === 'string') {
@@ -363789,7 +363435,7 @@ function extractErrorDetails(error) {
363789
363435
  const output = getFirstNonEmptyString(errorWithOutput.output, error.message);
363790
363436
  return { output: output ?? String(error), exitCode };
363791
363437
  }
363792
- if (utils_isRecord(error)) {
363438
+ if (isRecord(error)) {
363793
363439
  const output = getFirstNonEmptyString(error.output, error.message, error.error);
363794
363440
  if (output !== undefined) {
363795
363441
  return { output, exitCode };
@@ -713911,7 +713557,7 @@ const crsStatusSubcommand = {
713911
713557
  };
713912
713558
 
713913
713559
  ;// CONCATENATED MODULE: ./package.json
713914
- const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-patches-patch-claims-1"}');
713560
+ const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-features-traceability-00"}');
713915
713561
  ;// CONCATENATED MODULE: ../../package.json
713916
713562
  const package_namespaceObject_1 = {"i8":"2.10.0"};
713917
713563
  // EXTERNAL MODULE: ../../node_modules/semver/index.js