@firestartr/cli 2.11.0-snapshot-feature-git-traceability-1 → 2.11.0-snapshot-patches-patch-claims-2
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 +1340 -911
- package/build/packages/cdk8s_renderer/src/claims/github/repositoryFeature.d.ts +1 -3
- package/build/packages/cdk8s_renderer/src/claims/stitching/errors.d.ts +14 -0
- package/build/packages/cdk8s_renderer/src/claims/stitching/protectedPaths.d.ts +3 -0
- package/build/packages/cdk8s_renderer/src/claims/stitching/stitching.d.ts +19 -0
- package/build/packages/cdk8s_renderer/src/overriders/featureOverride.d.ts +2 -11
- package/build/packages/features_preparer/src/index.d.ts +5 -2
- package/build/packages/features_preparer/src/installer.d.ts +2 -4
- package/build/packages/features_preparer/src/renderer.d.ts +2 -0
- package/build/packages/features_renderer/index.d.ts +4 -1
- package/build/packages/features_renderer/src/auxiliar.d.ts +6 -12
- package/build/packages/features_renderer/src/render.d.ts +2 -1
- package/build/packages/features_renderer/src/schema.d.ts +8 -1
- package/package.json +1 -1
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}`.toLowerCase();
|
|
128398
|
+
const entityFolderName = `${entity.metadata?.name ?? entity.name}`.toLowerCase();
|
|
128399
128399
|
return external_path_default().join(basePath, entityFolderName, featureName);
|
|
128400
128400
|
}
|
|
128401
128401
|
function getFeatureRenderedConfigForComponent(entity, featureName, basePath = '/tmp/features') {
|
|
@@ -164488,771 +164488,6 @@ const base_secrets_schema = 'firestartr.dev://common/SecretsClaim';
|
|
|
164488
164488
|
SecretsClaimSchema: base_secrets_schema,
|
|
164489
164489
|
});
|
|
164490
164490
|
|
|
164491
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
|
|
164492
|
-
|
|
164493
|
-
|
|
164494
|
-
|
|
164495
|
-
|
|
164496
|
-
|
|
164497
|
-
|
|
164498
|
-
|
|
164499
|
-
|
|
164500
|
-
|
|
164501
|
-
|
|
164502
|
-
|
|
164503
|
-
|
|
164504
|
-
|
|
164505
|
-
|
|
164506
|
-
|
|
164507
|
-
|
|
164508
|
-
// We need to ensure the grep command is a gnu version
|
|
164509
|
-
let IS_GNU_GREP = false;
|
|
164510
|
-
let GREP_ON_CHECK = false;
|
|
164511
|
-
// this loop is for avoid race conditions
|
|
164512
|
-
// once a check is running no more checks
|
|
164513
|
-
async function waitForCheck() {
|
|
164514
|
-
const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
|
|
164515
|
-
do {
|
|
164516
|
-
await f_wait();
|
|
164517
|
-
} while (GREP_ON_CHECK);
|
|
164518
|
-
}
|
|
164519
|
-
function checkGrep() {
|
|
164520
|
-
return new Promise((ok, ko) => {
|
|
164521
|
-
// idempotency: already checked
|
|
164522
|
-
if (IS_GNU_GREP)
|
|
164523
|
-
return ok();
|
|
164524
|
-
// a check is already running no new checks
|
|
164525
|
-
if (GREP_ON_CHECK)
|
|
164526
|
-
return waitForCheck();
|
|
164527
|
-
logger.info('Checking the grep command');
|
|
164528
|
-
GREP_ON_CHECK = true;
|
|
164529
|
-
const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
|
|
164530
|
-
handler.on('close', (code) => {
|
|
164531
|
-
GREP_ON_CHECK = false;
|
|
164532
|
-
if (code === 0) {
|
|
164533
|
-
logger.debug('Grep is a gnu grep');
|
|
164534
|
-
IS_GNU_GREP = true;
|
|
164535
|
-
ok();
|
|
164536
|
-
}
|
|
164537
|
-
else {
|
|
164538
|
-
ko('Lazy evaluation needs a gnu-grep command');
|
|
164539
|
-
}
|
|
164540
|
-
});
|
|
164541
|
-
});
|
|
164542
|
-
}
|
|
164543
|
-
async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
|
|
164544
|
-
await checkGrep();
|
|
164545
|
-
let result = existingRefs;
|
|
164546
|
-
logger.info(`Load reference ${claimRef}`);
|
|
164547
|
-
initVirtualClaims(org);
|
|
164548
|
-
logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
|
|
164549
|
-
// cargas datos con grep
|
|
164550
|
-
try {
|
|
164551
|
-
const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
|
|
164552
|
-
const claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
|
|
164553
|
-
let variants = [];
|
|
164554
|
-
if (claim.kind === 'TFWorkspaceClaim' &&
|
|
164555
|
-
claim.providers?.terraform?.variants) {
|
|
164556
|
-
variants = claim.providers.terraform.variants;
|
|
164557
|
-
delete claim.providers.terraform.variants;
|
|
164558
|
-
}
|
|
164559
|
-
logger.silly(`Patched claim is:
|
|
164560
|
-
---
|
|
164561
|
-
${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
|
|
164562
|
-
try {
|
|
164563
|
-
validateClaim(claim, base[`${claim.kind}Schema`]);
|
|
164564
|
-
await optionalValidation(claim);
|
|
164565
|
-
}
|
|
164566
|
-
catch (error) {
|
|
164567
|
-
let errorMsg = '';
|
|
164568
|
-
if (Array.isArray(error)) {
|
|
164569
|
-
for (const data of error) {
|
|
164570
|
-
errorMsg = `${errorMsg}
|
|
164571
|
-
- ${data.instancePath}: ${data.message}`;
|
|
164572
|
-
}
|
|
164573
|
-
}
|
|
164574
|
-
else {
|
|
164575
|
-
errorMsg = error instanceof Error ? error.message : String(error);
|
|
164576
|
-
}
|
|
164577
|
-
throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
|
|
164578
|
-
}
|
|
164579
|
-
result[claimRef] = {};
|
|
164580
|
-
result[claimRef]['claim'] = claim;
|
|
164581
|
-
result[claimRef]['claimPath'] = VisitedClaims[claimRef];
|
|
164582
|
-
if (VisitedClaims[claimRef] === 'virtual') {
|
|
164583
|
-
result = await setVirtualClaimAdditionalData(result, claim, claimRef);
|
|
164584
|
-
}
|
|
164585
|
-
else {
|
|
164586
|
-
result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
|
|
164587
|
-
}
|
|
164588
|
-
const claimKind = claim.kind;
|
|
164589
|
-
const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
|
|
164590
|
-
for (const ref of references) {
|
|
164591
|
-
if (!result[ref]) {
|
|
164592
|
-
const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
|
|
164593
|
-
result = lodash_default().merge(result, resolvedReferences);
|
|
164594
|
-
}
|
|
164595
|
-
}
|
|
164596
|
-
if (variants.length > 0) {
|
|
164597
|
-
const parentClaimPath = VisitedClaims[claimRef];
|
|
164598
|
-
for (const variant of variants) {
|
|
164599
|
-
if (!variant.name || !variant.overrides) {
|
|
164600
|
-
throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
|
|
164601
|
-
}
|
|
164602
|
-
const prohibitedOverrideFields = ['source', 'module', 'name'];
|
|
164603
|
-
for (const field of prohibitedOverrideFields) {
|
|
164604
|
-
if (field in variant.overrides) {
|
|
164605
|
-
throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
|
|
164606
|
-
}
|
|
164607
|
-
}
|
|
164608
|
-
const composedName = claim.providers.terraform.name + '-' + variant.name;
|
|
164609
|
-
const variantClaim = lodash_default().cloneDeep(claim);
|
|
164610
|
-
variantClaim._parentClaimName = claim.name;
|
|
164611
|
-
variantClaim.name = composedName;
|
|
164612
|
-
variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
|
|
164613
|
-
variantClaim.providers.terraform.name = composedName;
|
|
164614
|
-
if (!variantClaim.annotations) {
|
|
164615
|
-
variantClaim.annotations = {};
|
|
164616
|
-
}
|
|
164617
|
-
variantClaim.annotations['firestartr.dev/variant-of'] =
|
|
164618
|
-
claim.providers.terraform.name;
|
|
164619
|
-
const variantRef = `TFWorkspaceClaim-${composedName}`;
|
|
164620
|
-
if (result[variantRef]) {
|
|
164621
|
-
throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
|
|
164622
|
-
}
|
|
164623
|
-
logger.info(`Creating synthetic variant claim ${variantRef}`);
|
|
164624
|
-
result[variantRef] = {};
|
|
164625
|
-
result[variantRef]['claim'] = variantClaim;
|
|
164626
|
-
result[variantRef]['claimPath'] = parentClaimPath;
|
|
164627
|
-
result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
|
|
164628
|
-
const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
|
|
164629
|
-
for (const ref of variantReferences) {
|
|
164630
|
-
if (!result[ref]) {
|
|
164631
|
-
const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
|
|
164632
|
-
result = lodash_default().merge(result, resolvedReferences);
|
|
164633
|
-
}
|
|
164634
|
-
}
|
|
164635
|
-
}
|
|
164636
|
-
}
|
|
164637
|
-
}
|
|
164638
|
-
catch (err) {
|
|
164639
|
-
throw `Lazy Loading: ${err}`;
|
|
164640
|
-
}
|
|
164641
|
-
return [result, postValidations];
|
|
164642
|
-
}
|
|
164643
|
-
let LoadedClaims = {};
|
|
164644
|
-
let VisitedClaims = {};
|
|
164645
|
-
let DuplicatedClaims = {};
|
|
164646
|
-
function resetLazyLoader() {
|
|
164647
|
-
LoadedClaims = {};
|
|
164648
|
-
VisitedClaims = {};
|
|
164649
|
-
DuplicatedClaims = {};
|
|
164650
|
-
}
|
|
164651
|
-
async function lazyGetClaim(kind, name, org, cwd) {
|
|
164652
|
-
const indice = `${kind}-${name}`;
|
|
164653
|
-
logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
|
|
164654
|
-
if (indice in LoadedClaims)
|
|
164655
|
-
return LoadedClaims[indice];
|
|
164656
|
-
if (isVirtualClaim(kind, name)) {
|
|
164657
|
-
await loadVirtualClaim(kind, name, org);
|
|
164658
|
-
}
|
|
164659
|
-
else {
|
|
164660
|
-
await getClaimsByName(name, cwd);
|
|
164661
|
-
}
|
|
164662
|
-
if (indice in LoadedClaims) {
|
|
164663
|
-
return LoadedClaims[indice];
|
|
164664
|
-
}
|
|
164665
|
-
else {
|
|
164666
|
-
throw new Error(`Error: ${kind}-${name} not found`);
|
|
164667
|
-
}
|
|
164668
|
-
}
|
|
164669
|
-
async function getClaimsByName(name, cwd = '.') {
|
|
164670
|
-
return new Promise((ok, ko) => {
|
|
164671
|
-
const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
|
|
164672
|
-
cwd: cwd,
|
|
164673
|
-
});
|
|
164674
|
-
logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
|
|
164675
|
-
const entradas = [];
|
|
164676
|
-
const chunks = [];
|
|
164677
|
-
handler.stdout.on('data', (data) => {
|
|
164678
|
-
chunks.push(data.toString('utf-8'));
|
|
164679
|
-
});
|
|
164680
|
-
handler.on('close', () => {
|
|
164681
|
-
const dataFinal = chunks.join('');
|
|
164682
|
-
const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
|
|
164683
|
-
for (const fileName of fileNameList) {
|
|
164684
|
-
// only yaml files
|
|
164685
|
-
if (!fileName.match(/\.yml$|\.yaml$/)) {
|
|
164686
|
-
continue;
|
|
164687
|
-
}
|
|
164688
|
-
// only entries not already visited
|
|
164689
|
-
if (Object.values(VisitedClaims).includes(fileName)) {
|
|
164690
|
-
continue;
|
|
164691
|
-
}
|
|
164692
|
-
entradas.push(external_path_.join(cwd, fileName));
|
|
164693
|
-
}
|
|
164694
|
-
return Promise.all(entradas.map((entrada) => {
|
|
164695
|
-
return loadRawClaim(entrada);
|
|
164696
|
-
}))
|
|
164697
|
-
.then(() => {
|
|
164698
|
-
ok();
|
|
164699
|
-
})
|
|
164700
|
-
.catch((err) => {
|
|
164701
|
-
ko(err);
|
|
164702
|
-
});
|
|
164703
|
-
});
|
|
164704
|
-
});
|
|
164705
|
-
}
|
|
164706
|
-
async function loadRawClaim(entry) {
|
|
164707
|
-
return new Promise((ok, ko) => {
|
|
164708
|
-
external_fs_.readFile(entry, 'utf-8', (error, data) => {
|
|
164709
|
-
if (error)
|
|
164710
|
-
return ko(`Reading ${entry}: ${error}`);
|
|
164711
|
-
const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
164712
|
-
if (!('kind' in claim && 'name' in claim)) {
|
|
164713
|
-
logger.warn(`Invalid claim file ${entry}`);
|
|
164714
|
-
}
|
|
164715
|
-
else {
|
|
164716
|
-
const claimKey = `${claim.kind}-${claim.name}`;
|
|
164717
|
-
if (Object.keys(DuplicatedClaims).includes(claimKey) &&
|
|
164718
|
-
DuplicatedClaims[claimKey] !== entry) {
|
|
164719
|
-
return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
|
|
164720
|
-
}
|
|
164721
|
-
LoadedClaims[claimKey] = data;
|
|
164722
|
-
VisitedClaims[claimKey] = entry;
|
|
164723
|
-
DuplicatedClaims[claimKey] = entry;
|
|
164724
|
-
ok();
|
|
164725
|
-
}
|
|
164726
|
-
});
|
|
164727
|
-
});
|
|
164728
|
-
}
|
|
164729
|
-
async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
|
|
164730
|
-
const virtualClaim = getVirtualClaim(claim.kind, claim.name);
|
|
164731
|
-
renderedData[claimRef]['initializers'] = [
|
|
164732
|
-
new UUIDInitializer(claim),
|
|
164733
|
-
new InitializerClaimRef(),
|
|
164734
|
-
new InitializerDefault(virtualClaim.getDefaultConfig()),
|
|
164735
|
-
];
|
|
164736
|
-
renderedData[claimRef]['globals'] = [];
|
|
164737
|
-
renderedData[claimRef]['overrides'] = [];
|
|
164738
|
-
renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
|
|
164739
|
-
return renderedData;
|
|
164740
|
-
}
|
|
164741
|
-
async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
|
|
164742
|
-
renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
|
|
164743
|
-
renderedData[claimRef]['globals'] = await loadGlobals(claim);
|
|
164744
|
-
renderedData[claimRef]['overrides'] = loadOverrides(claim);
|
|
164745
|
-
renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
|
|
164746
|
-
return renderedData;
|
|
164747
|
-
}
|
|
164748
|
-
async function loadVirtualClaim(kind, name, org) {
|
|
164749
|
-
const virtualClaim = getVirtualClaim(kind, name);
|
|
164750
|
-
const expandedClaim = await virtualClaim.expand({ org });
|
|
164751
|
-
logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
|
|
164752
|
-
LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
|
|
164753
|
-
VisitedClaims[`${kind}-${name}`] = 'virtual';
|
|
164754
|
-
}
|
|
164755
|
-
|
|
164756
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
|
|
164757
|
-
|
|
164758
|
-
|
|
164759
|
-
|
|
164760
|
-
/*
|
|
164761
|
-
* Default blocks are objects that cannot be merged but applied as a whole. Meaning:
|
|
164762
|
-
* - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
|
|
164763
|
-
* - If the block is missing at the claim-level, the default block is applied
|
|
164764
|
-
*/
|
|
164765
|
-
const defaultBlocksPaths = ['/providers/terraform/sync'];
|
|
164766
|
-
function applyBlockAwareDefaults(claim, defaultClaim) {
|
|
164767
|
-
// we always work with a clone of the original
|
|
164768
|
-
const claimClone = JSON.parse(JSON.stringify(claim));
|
|
164769
|
-
// if the block is claim-level defined we store it here
|
|
164770
|
-
const defaultBlocks = new Map();
|
|
164771
|
-
// we remove the blocks entirely of the claim
|
|
164772
|
-
// to achieve an add copy
|
|
164773
|
-
for (const defaultBlockPath of defaultBlocksPaths) {
|
|
164774
|
-
if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
|
|
164775
|
-
continue;
|
|
164776
|
-
const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
|
|
164777
|
-
if (originalValue) {
|
|
164778
|
-
logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
|
|
164779
|
-
defaultBlocks.set(defaultBlockPath, originalValue);
|
|
164780
|
-
fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
|
|
164781
|
-
}
|
|
164782
|
-
}
|
|
164783
|
-
// if the patch has the same path of a block
|
|
164784
|
-
// and the block was defined at claim-level
|
|
164785
|
-
// we interchange the value with the original value
|
|
164786
|
-
// otherwise we use the default
|
|
164787
|
-
const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
|
|
164788
|
-
.filter((jp) => jp.op === 'add')
|
|
164789
|
-
.map((jp) => {
|
|
164790
|
-
if (defaultBlocks.has(jp.path)) {
|
|
164791
|
-
return {
|
|
164792
|
-
...jp,
|
|
164793
|
-
value: defaultBlocks.get(jp.path),
|
|
164794
|
-
};
|
|
164795
|
-
}
|
|
164796
|
-
else {
|
|
164797
|
-
return jp;
|
|
164798
|
-
}
|
|
164799
|
-
});
|
|
164800
|
-
return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
|
|
164801
|
-
}
|
|
164802
|
-
function claimsDefaulter_hasDeepPath(data, deepPath) {
|
|
164803
|
-
return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
|
|
164804
|
-
}
|
|
164805
|
-
|
|
164806
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
|
|
164807
|
-
|
|
164808
|
-
|
|
164809
|
-
|
|
164810
|
-
|
|
164811
|
-
|
|
164812
|
-
|
|
164813
|
-
|
|
164814
|
-
|
|
164815
|
-
|
|
164816
|
-
|
|
164817
|
-
|
|
164818
|
-
|
|
164819
|
-
|
|
164820
|
-
|
|
164821
|
-
|
|
164822
|
-
const isYamlFile = new RegExp(/\.(yaml|yml)$/);
|
|
164823
|
-
/**
|
|
164824
|
-
*
|
|
164825
|
-
* @returns Virtual Claims
|
|
164826
|
-
*/
|
|
164827
|
-
const virtualClaims = [new FirestartrAllClaim()];
|
|
164828
|
-
/*
|
|
164829
|
-
* Loads all applicable globals for a given claim and stores them into a list.
|
|
164830
|
-
*
|
|
164831
|
-
* Input:
|
|
164832
|
-
* - claim: our custom claim object, with which we check if the global is applicable
|
|
164833
|
-
*
|
|
164834
|
-
* Returns:
|
|
164835
|
-
* - A GlobalSection array promise, containing all the globals loaded for this claim
|
|
164836
|
-
*
|
|
164837
|
-
*/
|
|
164838
|
-
async function loadGlobals(claim) {
|
|
164839
|
-
const result = [];
|
|
164840
|
-
await crawl(getPath('globals'), (entry) => {
|
|
164841
|
-
return isYamlFile.test(entry);
|
|
164842
|
-
}, async (entry, data) => {
|
|
164843
|
-
const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
164844
|
-
const ext = external_path_.extname(entry);
|
|
164845
|
-
const name = external_path_.basename(entry, ext);
|
|
164846
|
-
if (name in SECTIONS_BY_FILE_NAME) {
|
|
164847
|
-
const section = new SECTIONS_BY_FILE_NAME[name](globalData);
|
|
164848
|
-
if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
|
|
164849
|
-
throw new Error(`Invalid ${name} global file`);
|
|
164850
|
-
}
|
|
164851
|
-
if (globalData.kind === claim.kind) {
|
|
164852
|
-
result.push(section);
|
|
164853
|
-
}
|
|
164854
|
-
else {
|
|
164855
|
-
logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
|
|
164856
|
-
}
|
|
164857
|
-
}
|
|
164858
|
-
});
|
|
164859
|
-
logger.info(`Loaded globals ${result}`);
|
|
164860
|
-
return result;
|
|
164861
|
-
}
|
|
164862
|
-
/*
|
|
164863
|
-
* If the input claim is a GithubRepositoryClaim, it loads all it's applicable
|
|
164864
|
-
* overrides and stores them into a list. Otherwise, it returns an empty list.
|
|
164865
|
-
*
|
|
164866
|
-
* Input:
|
|
164867
|
-
* - claim: our custom claim object, which should be a GithubRepositoryClaim
|
|
164868
|
-
*
|
|
164869
|
-
* Returns:
|
|
164870
|
-
* - An Overrider array, with a GithubRepositoryOverrider if the claim
|
|
164871
|
-
* was a GithubRepositoryClaim, or empty if it was not.
|
|
164872
|
-
*
|
|
164873
|
-
*/
|
|
164874
|
-
function loadOverrides(claim) {
|
|
164875
|
-
const overrides = [];
|
|
164876
|
-
if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
|
|
164877
|
-
overrides.push(new GithubRepositoryOverrider());
|
|
164878
|
-
}
|
|
164879
|
-
return overrides;
|
|
164880
|
-
}
|
|
164881
|
-
/**
|
|
164882
|
-
* Load all normalizers and stores them into a list.
|
|
164883
|
-
* @param claim
|
|
164884
|
-
* @returns
|
|
164885
|
-
*/
|
|
164886
|
-
async function loadNormalizers(_claim, path) {
|
|
164887
|
-
const result = [];
|
|
164888
|
-
for (const normalizer of NORMALIZERS) {
|
|
164889
|
-
result.push(new normalizer({ path: path }));
|
|
164890
|
-
}
|
|
164891
|
-
return result;
|
|
164892
|
-
}
|
|
164893
|
-
/*
|
|
164894
|
-
* Loads all applicable initializers for a given claim and stores them into a list.
|
|
164895
|
-
*
|
|
164896
|
-
* Input:
|
|
164897
|
-
* - claim: our custom claim object, with which we check if the initializer is applicable
|
|
164898
|
-
*
|
|
164899
|
-
* Returns:
|
|
164900
|
-
* - An Initializer array promise, containing all the initializers loaded for this claim
|
|
164901
|
-
*
|
|
164902
|
-
*/
|
|
164903
|
-
let loadedInitializers = false;
|
|
164904
|
-
async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
|
|
164905
|
-
const result = [];
|
|
164906
|
-
await crawl(initializersPath, (entry) => {
|
|
164907
|
-
return isYamlFile.test(entry);
|
|
164908
|
-
}, async (entry, data) => {
|
|
164909
|
-
const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
164910
|
-
const ext = external_path_.extname(entry);
|
|
164911
|
-
const name = external_path_.basename(entry, ext);
|
|
164912
|
-
if (name in INITIALIZERS_BY_FILE_NAME) {
|
|
164913
|
-
const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
|
|
164914
|
-
if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
|
|
164915
|
-
throw new Error(`Invalid ${name} initializer file`);
|
|
164916
|
-
}
|
|
164917
|
-
if (initializerData.kind === claim.kind) {
|
|
164918
|
-
result.push(initializerBfN);
|
|
164919
|
-
}
|
|
164920
|
-
else {
|
|
164921
|
-
logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
|
|
164922
|
-
}
|
|
164923
|
-
}
|
|
164924
|
-
});
|
|
164925
|
-
for (const initializer of INITIALIZERS) {
|
|
164926
|
-
if (initializer.applicableKinds.includes(claim.kind)) {
|
|
164927
|
-
result.push(new initializer({ path: claimPath }));
|
|
164928
|
-
}
|
|
164929
|
-
}
|
|
164930
|
-
logger.info(`Loaded initializers ${JSON.stringify(result)}`);
|
|
164931
|
-
loadedInitializers = result;
|
|
164932
|
-
return loadedInitializers;
|
|
164933
|
-
}
|
|
164934
|
-
function loadClaimDefaults() {
|
|
164935
|
-
try {
|
|
164936
|
-
return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
|
|
164937
|
-
}
|
|
164938
|
-
catch (error) {
|
|
164939
|
-
const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
|
|
164940
|
-
logger.error(errMsg);
|
|
164941
|
-
throw new Error(errMsg);
|
|
164942
|
-
}
|
|
164943
|
-
}
|
|
164944
|
-
/*
|
|
164945
|
-
* Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
|
|
164946
|
-
*
|
|
164947
|
-
* Input:
|
|
164948
|
-
* - claim: our custom claim object, which we want to patch
|
|
164949
|
-
* - defaultsClaims: an object, containing the defaults for each claim kind,
|
|
164950
|
-
* with the same structure that we use for the actual claims
|
|
164951
|
-
*
|
|
164952
|
-
* Returns:
|
|
164953
|
-
* - The patched claim
|
|
164954
|
-
*
|
|
164955
|
-
*/
|
|
164956
|
-
function loader_patchClaim(claim, defaultsClaims) {
|
|
164957
|
-
if (defaultsClaims[claim.kind]) {
|
|
164958
|
-
claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
|
|
164959
|
-
}
|
|
164960
|
-
return claim;
|
|
164961
|
-
}
|
|
164962
|
-
async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
|
|
164963
|
-
const result = {};
|
|
164964
|
-
const fullResult = {};
|
|
164965
|
-
await crawlWithExclusions(crsPath, (entry) => {
|
|
164966
|
-
return isYamlFile.test(entry);
|
|
164967
|
-
}, async (entry, data) => {
|
|
164968
|
-
const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
|
|
164969
|
-
const metadata = yamlData?.metadata;
|
|
164970
|
-
if (!(yamlData &&
|
|
164971
|
-
typeof yamlData === 'object' &&
|
|
164972
|
-
'kind' in yamlData &&
|
|
164973
|
-
metadata &&
|
|
164974
|
-
typeof metadata === 'object' &&
|
|
164975
|
-
'name' in metadata)) {
|
|
164976
|
-
logger.warn(`Invalid CR file ${entry}`);
|
|
164977
|
-
}
|
|
164978
|
-
else {
|
|
164979
|
-
const crKey = `${yamlData.kind}-${metadata.name}`;
|
|
164980
|
-
if (fullResult[crKey]) {
|
|
164981
|
-
logger.error(`Duplicate CR ${crKey}`);
|
|
164982
|
-
}
|
|
164983
|
-
// Keep the complete CR set for validations that need repository-wide context.
|
|
164984
|
-
fullResult[crKey] = yamlData;
|
|
164985
|
-
// Keep the filtered CR set for previous-CR lookup during claim rendering.
|
|
164986
|
-
if (allowedClaimReferences.length === 0 ||
|
|
164987
|
-
allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
|
|
164988
|
-
result[crKey] = yamlData;
|
|
164989
|
-
}
|
|
164990
|
-
}
|
|
164991
|
-
}, excludedPaths.concat(getAdditionalPaths()));
|
|
164992
|
-
return { crs: result, fullCrs: fullResult };
|
|
164993
|
-
}
|
|
164994
|
-
async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
|
|
164995
|
-
const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
|
|
164996
|
-
return crs;
|
|
164997
|
-
}
|
|
164998
|
-
/*
|
|
164999
|
-
* Description: Get the renamed using claimRef annotation
|
|
165000
|
-
* Input: claims: any, crs: any
|
|
165001
|
-
* Output: renames: IRenameResult[]
|
|
165002
|
-
*/
|
|
165003
|
-
async function loadRenames(claims, crs) {
|
|
165004
|
-
/*
|
|
165005
|
-
* If checkRenames is false, we don't need to check for renames
|
|
165006
|
-
*/
|
|
165007
|
-
const renamesEnabled = getRenamesEnabled();
|
|
165008
|
-
if (!renamesEnabled)
|
|
165009
|
-
return [];
|
|
165010
|
-
const result = [];
|
|
165011
|
-
/**
|
|
165012
|
-
* For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
|
|
165013
|
-
*/
|
|
165014
|
-
for (const cr of Object.values(crs)) {
|
|
165015
|
-
const anyCr = cr;
|
|
165016
|
-
if (!anyCr?.metadata?.annotations)
|
|
165017
|
-
continue;
|
|
165018
|
-
// if the annotation is a claimRef
|
|
165019
|
-
const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
|
|
165020
|
-
const annotations = anyCr?.metadata?.annotations || {};
|
|
165021
|
-
if (annotationName in annotations) {
|
|
165022
|
-
const claimRef = anyCr.metadata.annotations[annotationName];
|
|
165023
|
-
// split the annotation into kind and name
|
|
165024
|
-
const [kind, name] = claimRef.split('/');
|
|
165025
|
-
// if the claim exists, check if the name is different for the given provider
|
|
165026
|
-
const referencedClaim = claims[`${kind}-${name}`].claim;
|
|
165027
|
-
const oldName = anyCr.metadata.name;
|
|
165028
|
-
const newName = referencedClaim.providers[getConfiguredProvider()].name;
|
|
165029
|
-
if (oldName !== newName) {
|
|
165030
|
-
result.push({
|
|
165031
|
-
kind: referencedClaim.kind,
|
|
165032
|
-
claimPath: claims[`${kind}-${name}`].claimPath,
|
|
165033
|
-
claimName: referencedClaim.name,
|
|
165034
|
-
oldName,
|
|
165035
|
-
newName,
|
|
165036
|
-
});
|
|
165037
|
-
}
|
|
165038
|
-
}
|
|
165039
|
-
}
|
|
165040
|
-
return result;
|
|
165041
|
-
}
|
|
165042
|
-
async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
|
|
165043
|
-
const data = {
|
|
165044
|
-
renderClaims: {},
|
|
165045
|
-
crs: {},
|
|
165046
|
-
fullCrs: {},
|
|
165047
|
-
};
|
|
165048
|
-
const defaults = loadClaimDefaults();
|
|
165049
|
-
for await (const claimRef of claimRefList) {
|
|
165050
|
-
const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
|
|
165051
|
-
data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
|
|
165052
|
-
}
|
|
165053
|
-
const crClaimReferences = [];
|
|
165054
|
-
for (const ref of Object.keys(data.renderClaims)) {
|
|
165055
|
-
// Replaces only the first instance of '-'
|
|
165056
|
-
crClaimReferences.push(ref.replace('-', '/'));
|
|
165057
|
-
}
|
|
165058
|
-
const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
|
|
165059
|
-
data['crs'] = loadedCrs.crs;
|
|
165060
|
-
data['fullCrs'] = loadedCrs.fullCrs;
|
|
165061
|
-
return data;
|
|
165062
|
-
}
|
|
165063
|
-
function getOrg() {
|
|
165064
|
-
const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
|
|
165065
|
-
return groupOrg;
|
|
165066
|
-
}
|
|
165067
|
-
|
|
165068
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
|
|
165069
|
-
function validateTfStateKeyUniqueness(crs) {
|
|
165070
|
-
const tfStateKeys = {};
|
|
165071
|
-
for (const crKey of Object.keys(crs)) {
|
|
165072
|
-
const cr = crs[crKey];
|
|
165073
|
-
if (!isTerraformWorkspace(cr))
|
|
165074
|
-
continue;
|
|
165075
|
-
const tfKey = cr.spec.firestartr.tfStateKey;
|
|
165076
|
-
if (tfStateKeys[tfKey]) {
|
|
165077
|
-
throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
|
|
165078
|
-
|
|
165079
|
-
The tfStateKey ${tfKey} is not unique,
|
|
165080
|
-
|
|
165081
|
-
It is being used by the following CRs:
|
|
165082
|
-
|
|
165083
|
-
- ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
|
|
165084
|
-
|
|
165085
|
-
- ${cr.kind} ${cr.metadata.name}
|
|
165086
|
-
|
|
165087
|
-
Check the following Claims to ensure that the tfStateKey is unique:
|
|
165088
|
-
|
|
165089
|
-
- ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
|
|
165090
|
-
|
|
165091
|
-
- ${cr.metadata.annotations['firestartr.dev/claim-ref']}
|
|
165092
|
-
`);
|
|
165093
|
-
}
|
|
165094
|
-
tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
|
|
165095
|
-
}
|
|
165096
|
-
}
|
|
165097
|
-
function references_isCatalogEntity(cr) {
|
|
165098
|
-
return [
|
|
165099
|
-
'Component',
|
|
165100
|
-
'Domain',
|
|
165101
|
-
'System',
|
|
165102
|
-
'User',
|
|
165103
|
-
'Group',
|
|
165104
|
-
'Resource',
|
|
165105
|
-
].includes(cr.kind);
|
|
165106
|
-
}
|
|
165107
|
-
function isTerraformWorkspace(cr) {
|
|
165108
|
-
return cr.kind === 'FirestartrTerraformWorkspace';
|
|
165109
|
-
}
|
|
165110
|
-
|
|
165111
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
|
|
165112
|
-
|
|
165113
|
-
const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
|
|
165114
|
-
function validateCrSizes(crs) {
|
|
165115
|
-
for (const key of Object.keys(crs)) {
|
|
165116
|
-
const cr = crs[key];
|
|
165117
|
-
const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
|
|
165118
|
-
const size = Buffer.byteLength(serialized, 'utf8');
|
|
165119
|
-
if (size > K8S_OBJECT_SIZE_LIMIT) {
|
|
165120
|
-
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).`);
|
|
165121
|
-
}
|
|
165122
|
-
}
|
|
165123
|
-
}
|
|
165124
|
-
|
|
165125
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
|
|
165126
|
-
|
|
165127
|
-
function validatePermissionsUniqueness(crs) {
|
|
165128
|
-
for (const crkey in crs) {
|
|
165129
|
-
const cr = crs[crkey];
|
|
165130
|
-
if (cr.kind === 'FirestartrGithubRepository') {
|
|
165131
|
-
const permissions = cr.spec.permissions;
|
|
165132
|
-
logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
|
|
165133
|
-
if (permissions) {
|
|
165134
|
-
const rolesByIdentifier = new Map();
|
|
165135
|
-
for (const perm of permissions) {
|
|
165136
|
-
const identifier = perm.collaborator
|
|
165137
|
-
? perm.collaborator
|
|
165138
|
-
: `ref:${perm.ref.kind}:${perm.ref.name}`;
|
|
165139
|
-
const role = perm.role;
|
|
165140
|
-
const prevRole = rolesByIdentifier.get(identifier);
|
|
165141
|
-
if (prevRole === undefined) {
|
|
165142
|
-
rolesByIdentifier.set(identifier, role);
|
|
165143
|
-
}
|
|
165144
|
-
else if (prevRole !== role) {
|
|
165145
|
-
throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
|
|
165146
|
-
}
|
|
165147
|
-
}
|
|
165148
|
-
}
|
|
165149
|
-
}
|
|
165150
|
-
}
|
|
165151
|
-
}
|
|
165152
|
-
|
|
165153
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
|
|
165154
|
-
const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
|
|
165155
|
-
const CATALOG_KINDS = new Set([
|
|
165156
|
-
'User',
|
|
165157
|
-
'Group',
|
|
165158
|
-
'Component',
|
|
165159
|
-
'System',
|
|
165160
|
-
'Domain',
|
|
165161
|
-
'API',
|
|
165162
|
-
'Resource',
|
|
165163
|
-
'CatalogKubernetesResource',
|
|
165164
|
-
'CatalogTerraformWorkspace',
|
|
165165
|
-
'CatalogArgoDeploy',
|
|
165166
|
-
'CatalogOrgWebhook',
|
|
165167
|
-
'CatalogSecrets',
|
|
165168
|
-
]);
|
|
165169
|
-
function validateNoBackstageAnnotationsInK8sCrs(crs) {
|
|
165170
|
-
for (const crKey in crs) {
|
|
165171
|
-
const cr = crs[crKey];
|
|
165172
|
-
if (CATALOG_KINDS.has(cr.kind))
|
|
165173
|
-
continue;
|
|
165174
|
-
if (cr.metadata?.annotations) {
|
|
165175
|
-
const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
|
|
165176
|
-
if (backstageKeys.length > 0) {
|
|
165177
|
-
throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
|
|
165178
|
-
`Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
|
|
165179
|
-
}
|
|
165180
|
-
}
|
|
165181
|
-
}
|
|
165182
|
-
}
|
|
165183
|
-
|
|
165184
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
|
|
165185
|
-
|
|
165186
|
-
/**
|
|
165187
|
-
* @param renderClaims renderClaims to sort
|
|
165188
|
-
* @param kinds kinds to get from renderClaims
|
|
165189
|
-
* @returns renderClaims with only the kinds specified
|
|
165190
|
-
*/
|
|
165191
|
-
function sortRenderClaimsByKind(renderClaims, kinds) {
|
|
165192
|
-
let result = {};
|
|
165193
|
-
for (const kind of kinds) {
|
|
165194
|
-
for (const key in renderClaims) {
|
|
165195
|
-
if (renderClaims[key].claim.kind === kind) {
|
|
165196
|
-
result[key] = renderClaims[key];
|
|
165197
|
-
}
|
|
165198
|
-
}
|
|
165199
|
-
result = sortSelfKindRefs(result, kind);
|
|
165200
|
-
}
|
|
165201
|
-
return result;
|
|
165202
|
-
}
|
|
165203
|
-
function sortSelfKindRefs(renderClaims, kind) {
|
|
165204
|
-
if (!hasSelfRefs(kind)) {
|
|
165205
|
-
return renderClaims;
|
|
165206
|
-
}
|
|
165207
|
-
const newRenderClaims = {};
|
|
165208
|
-
const data = extractRefs(renderClaims, kind);
|
|
165209
|
-
const ordered = [];
|
|
165210
|
-
const visiting = [];
|
|
165211
|
-
const visited = [];
|
|
165212
|
-
for (const key of Object.keys(data)) {
|
|
165213
|
-
if (!visited.find((obj) => obj.name === key)) {
|
|
165214
|
-
sortRefs(data, data[key], ordered, visiting, visited);
|
|
165215
|
-
}
|
|
165216
|
-
}
|
|
165217
|
-
for (const cr of ordered) {
|
|
165218
|
-
newRenderClaims[cr.name] = renderClaims[cr.name];
|
|
165219
|
-
}
|
|
165220
|
-
return newRenderClaims;
|
|
165221
|
-
}
|
|
165222
|
-
function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
|
|
165223
|
-
if (visiting.find((obj) => obj.name === claim.name)) {
|
|
165224
|
-
throw new Error(`Circular dependency detected for '${claim.name}'`);
|
|
165225
|
-
}
|
|
165226
|
-
if (!visited.find((obj) => obj.name === claim.name)) {
|
|
165227
|
-
visiting.push(claim);
|
|
165228
|
-
if (claim.refs) {
|
|
165229
|
-
for (const ref of claim.refs) {
|
|
165230
|
-
const referencedObject = data[ref];
|
|
165231
|
-
if (!referencedObject) {
|
|
165232
|
-
throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
|
|
165233
|
-
}
|
|
165234
|
-
sortRefs(data, referencedObject, ordered, visiting, visited);
|
|
165235
|
-
}
|
|
165236
|
-
}
|
|
165237
|
-
visited.push(visiting.pop());
|
|
165238
|
-
ordered.push(claim);
|
|
165239
|
-
}
|
|
165240
|
-
}
|
|
165241
|
-
function hasSelfRefs(kind) {
|
|
165242
|
-
return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
|
|
165243
|
-
}
|
|
165244
|
-
|
|
165245
|
-
;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
|
|
165246
|
-
|
|
165247
|
-
function sortKinds(orderedKinds, claims) {
|
|
165248
|
-
const result = [];
|
|
165249
|
-
for (const kind of orderedKinds) {
|
|
165250
|
-
const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
|
|
165251
|
-
result.push(kindRenderClaims);
|
|
165252
|
-
}
|
|
165253
|
-
return result;
|
|
165254
|
-
}
|
|
165255
|
-
|
|
165256
164491
|
;// CONCATENATED MODULE: ../features_renderer/src/common.ts
|
|
165257
164492
|
|
|
165258
164493
|
function isDirectory(path) {
|
|
@@ -165263,7 +164498,7 @@ function isFile(path) {
|
|
|
165263
164498
|
}
|
|
165264
164499
|
function common_slurpFile(path) {
|
|
165265
164500
|
if (!isFile(path)) {
|
|
165266
|
-
throw `File not found ${path}
|
|
164501
|
+
throw new Error(`File not found ${path}`);
|
|
165267
164502
|
}
|
|
165268
164503
|
return external_fs_.readFileSync(path, { encoding: 'utf-8' });
|
|
165269
164504
|
}
|
|
@@ -165293,13 +164528,23 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
|
|
|
165293
164528
|
},
|
|
165294
164529
|
},
|
|
165295
164530
|
patches: {
|
|
164531
|
+
// Flat array of claim-level JSON Patch operations, applied to the
|
|
164532
|
+
// claim during claim stitching via claim-relative pointers (e.g.
|
|
164533
|
+
// /annotations/backstage.io~1techdocs-ref). This is the canonical
|
|
164534
|
+
// source for claim-level feature behaviour.
|
|
164535
|
+
type: 'array',
|
|
164536
|
+
items: { $ref: '#/definitions/Patch' },
|
|
164537
|
+
},
|
|
164538
|
+
claimPatches: {
|
|
164539
|
+
// Provider-keyed object consumed when a feature claim is created
|
|
164540
|
+
// elsewhere (attached at the top level of the created claim). This is
|
|
164541
|
+
// distinct from `patches`, which claim stitching consumes directly.
|
|
165296
164542
|
type: 'object',
|
|
164543
|
+
additionalProperties: false,
|
|
165297
164544
|
patternProperties: {
|
|
165298
164545
|
'^.+$': {
|
|
165299
164546
|
type: 'array',
|
|
165300
|
-
items: {
|
|
165301
|
-
$ref: '#/definitions/Patch',
|
|
165302
|
-
},
|
|
164547
|
+
items: { $ref: '#/definitions/Patch' },
|
|
165303
164548
|
},
|
|
165304
164549
|
},
|
|
165305
164550
|
},
|
|
@@ -165403,7 +164648,7 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
|
|
|
165403
164648
|
type: 'string',
|
|
165404
164649
|
},
|
|
165405
164650
|
value: {
|
|
165406
|
-
type: 'string',
|
|
164651
|
+
type: ['string', 'object', 'array', 'number', 'boolean', 'null'],
|
|
165407
164652
|
},
|
|
165408
164653
|
},
|
|
165409
164654
|
required: ['name', 'op', 'path'],
|
|
@@ -165428,10 +164673,10 @@ function validate(featurePath) {
|
|
|
165428
164673
|
}
|
|
165429
164674
|
function __validateDirStructure(featurePath) {
|
|
165430
164675
|
if (!isDirectory(featurePath)) {
|
|
165431
|
-
throw `Feature: ${featurePath} not a directory
|
|
164676
|
+
throw new Error(`Feature: ${featurePath} not a directory`);
|
|
165432
164677
|
}
|
|
165433
164678
|
if (!isFile(external_path_.join(featurePath, 'config.yaml'))) {
|
|
165434
|
-
throw `Feature: ${featurePath}/config.yaml not found or not a file
|
|
164679
|
+
throw new Error(`Feature: ${featurePath}/config.yaml not found or not a file`);
|
|
165435
164680
|
}
|
|
165436
164681
|
}
|
|
165437
164682
|
function __validateFeatureConfig(featurePath) {
|
|
@@ -165441,10 +164686,16 @@ function __validateFeatureConfig(featurePath) {
|
|
|
165441
164686
|
return configData;
|
|
165442
164687
|
}
|
|
165443
164688
|
catch (error) {
|
|
165444
|
-
throw `Feature: loading config.yaml: ${error}
|
|
164689
|
+
throw new Error(`Feature: loading config.yaml: ${error}`);
|
|
165445
164690
|
}
|
|
165446
164691
|
}
|
|
165447
164692
|
function __validateFeatureConfigData(configData) {
|
|
164693
|
+
// Detect old provider-keyed patches shape for migration hint (PLAN #2648)
|
|
164694
|
+
if (configData.patches &&
|
|
164695
|
+
!Array.isArray(configData.patches) &&
|
|
164696
|
+
typeof configData.patches === 'object') {
|
|
164697
|
+
throw new Error(`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]`);
|
|
164698
|
+
}
|
|
165448
164699
|
const ajv = new (ajv_default())({
|
|
165449
164700
|
allowUnionTypes: true,
|
|
165450
164701
|
});
|
|
@@ -165455,7 +164706,7 @@ function __validateFeatureConfigData(configData) {
|
|
|
165455
164706
|
const validate = ajv.compile(activeSchema);
|
|
165456
164707
|
const valid = validate(configData);
|
|
165457
164708
|
if (!valid) {
|
|
165458
|
-
throw `Feature: config.yaml invalid ${JSON.stringify(validate.errors)}
|
|
164709
|
+
throw new Error(`Feature: config.yaml invalid ${JSON.stringify(validate.errors)}`);
|
|
165459
164710
|
}
|
|
165460
164711
|
}
|
|
165461
164712
|
function stripAnchoringConstraints(schemaDef) {
|
|
@@ -165466,14 +164717,12 @@ function stripAnchoringConstraints(schemaDef) {
|
|
|
165466
164717
|
return clone;
|
|
165467
164718
|
}
|
|
165468
164719
|
function __validateJsonPatches(configData) {
|
|
165469
|
-
const
|
|
165470
|
-
|
|
165471
|
-
|
|
165472
|
-
|
|
165473
|
-
|
|
165474
|
-
|
|
165475
|
-
}
|
|
165476
|
-
}
|
|
164720
|
+
const patches = configData.patches;
|
|
164721
|
+
if (!Array.isArray(patches))
|
|
164722
|
+
return;
|
|
164723
|
+
const err = fast_json_patch_default().validate(patches);
|
|
164724
|
+
if (err) {
|
|
164725
|
+
throw new Error(`Feature: config.yaml -> patches: invalid patches ${JSON.stringify(err.message ?? err)}`);
|
|
165477
164726
|
}
|
|
165478
164727
|
}
|
|
165479
164728
|
|
|
@@ -166259,19 +165508,12 @@ function addTraceability(context, src, content) {
|
|
|
166259
165508
|
}
|
|
166260
165509
|
function addTraceabilityStamp(context, content) {
|
|
166261
165510
|
const traceability = context.traceability || {};
|
|
166262
|
-
const
|
|
166263
|
-
|
|
166264
|
-
|
|
166265
|
-
|
|
166266
|
-
|
|
166267
|
-
|
|
166268
|
-
if (traceability.sha) {
|
|
166269
|
-
stampLines.push(`# FEATURE_GIT_SHA: "${traceability.sha}"`);
|
|
166270
|
-
}
|
|
166271
|
-
if (Array.isArray(traceability.tags) && traceability.tags.length > 0) {
|
|
166272
|
-
stampLines.push(`# FEATURE_GIT_TAGS: ${JSON.stringify(traceability.tags)}`);
|
|
166273
|
-
}
|
|
166274
|
-
const stamp = `${stampLines.join('\n')}\n\n`;
|
|
165511
|
+
const stamp = `---
|
|
165512
|
+
# FEATURE_NAME: ${traceability.name}
|
|
165513
|
+
# FEATURE_VERSION: ${traceability.version}
|
|
165514
|
+
# FEATURE_URL: ${traceability.url}
|
|
165515
|
+
|
|
165516
|
+
`;
|
|
166275
165517
|
const output = [];
|
|
166276
165518
|
let replaced = false;
|
|
166277
165519
|
for (const line of content.split(/\n/)) {
|
|
@@ -166318,10 +165560,12 @@ const renderTestsSchema = {
|
|
|
166318
165560
|
items: {
|
|
166319
165561
|
type: 'object',
|
|
166320
165562
|
additionalProperties: false,
|
|
166321
|
-
required: ['name'
|
|
165563
|
+
required: ['name'],
|
|
165564
|
+
anyOf: [{ required: ['cr'] }, { required: ['claim'] }],
|
|
166322
165565
|
properties: {
|
|
166323
165566
|
name: { type: 'string', minLength: 1 },
|
|
166324
165567
|
cr: { type: 'string', minLength: 1 },
|
|
165568
|
+
claim: { type: 'string', minLength: 1 },
|
|
166325
165569
|
args: { type: 'object' },
|
|
166326
165570
|
},
|
|
166327
165571
|
},
|
|
@@ -166366,16 +165610,22 @@ function loadAndValidateRenderTests(featurePath) {
|
|
|
166366
165610
|
ensureUniqueTestNames(doc);
|
|
166367
165611
|
return doc;
|
|
166368
165612
|
}
|
|
166369
|
-
function
|
|
166370
|
-
if (external_node_path_namespaceObject.isAbsolute(
|
|
166371
|
-
throw new Error(
|
|
165613
|
+
function resolvePathRelativeToFeature(featurePath, relPath, kind) {
|
|
165614
|
+
if (external_node_path_namespaceObject.isAbsolute(relPath)) {
|
|
165615
|
+
throw new Error(`${kind} path must be relative to the feature root, got absolute: ${relPath}`);
|
|
166372
165616
|
}
|
|
166373
|
-
const resolved = external_node_path_namespaceObject.resolve(featurePath,
|
|
165617
|
+
const resolved = external_node_path_namespaceObject.resolve(featurePath, relPath);
|
|
166374
165618
|
if (!external_node_fs_namespaceObject.existsSync(resolved)) {
|
|
166375
|
-
throw new Error(
|
|
165619
|
+
throw new Error(`${kind} file not found (resolved from "${relPath}"): ${resolved}`);
|
|
166376
165620
|
}
|
|
166377
165621
|
return resolved;
|
|
166378
165622
|
}
|
|
165623
|
+
function resolveCrPath(featurePath, crRelPath) {
|
|
165624
|
+
return resolvePathRelativeToFeature(featurePath, crRelPath, 'CR');
|
|
165625
|
+
}
|
|
165626
|
+
function resolveClaimPath(featurePath, claimRelPath) {
|
|
165627
|
+
return resolvePathRelativeToFeature(featurePath, claimRelPath, 'claim');
|
|
165628
|
+
}
|
|
166379
165629
|
function listYamlFiles(dir) {
|
|
166380
165630
|
if (!fs.existsSync(dir))
|
|
166381
165631
|
return [];
|
|
@@ -166440,7 +165690,7 @@ function buildExpectedOutput(config, renderDir) {
|
|
|
166440
165690
|
return {
|
|
166441
165691
|
files,
|
|
166442
165692
|
patches: config.patches || [],
|
|
166443
|
-
|
|
165693
|
+
claimPatches: config.claimPatches || {},
|
|
166444
165694
|
};
|
|
166445
165695
|
}
|
|
166446
165696
|
/* ---------- Context-style API for a render temp dir ---------- */
|
|
@@ -166494,6 +165744,7 @@ async function createRenderContext(prefix = 'feature-render-') {
|
|
|
166494
165744
|
buildExpectedOutput,
|
|
166495
165745
|
loadAndValidateRenderTests,
|
|
166496
165746
|
resolveCrPath,
|
|
165747
|
+
resolveClaimPath,
|
|
166497
165748
|
});
|
|
166498
165749
|
|
|
166499
165750
|
;// CONCATENATED MODULE: ../features_renderer/src/featureArgsValidation.ts
|
|
@@ -166586,7 +165837,19 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
|
|
|
166586
165837
|
resolveInside(featureRenderPath, dest, 'file dest', 'render target directory');
|
|
166587
165838
|
}
|
|
166588
165839
|
}
|
|
166589
|
-
const output = { files: [] };
|
|
165840
|
+
const output = { files: [], claimPatches: {} };
|
|
165841
|
+
// `patches` is the flat array of claim-level JSON Patch operations. Claim
|
|
165842
|
+
// stitching (`extractClaimPatches`/`collectFeaturePatches` in cdk8s_renderer)
|
|
165843
|
+
// consumes `output.patches` as the canonical claim-level source.
|
|
165844
|
+
output.patches = context.config.patches;
|
|
165845
|
+
// `claimPatches` is provider-keyed and is a separate attach path used when
|
|
165846
|
+
// a feature claim is created elsewhere (via `renderFeature`). It is not the
|
|
165847
|
+
// claim-stitching source; that is `patches` above. Expose it in the render
|
|
165848
|
+
// output only when present. We do not copy `patches` into `claimPatches`.
|
|
165849
|
+
if (context.config.claimPatches &&
|
|
165850
|
+
typeof context.config.claimPatches === 'object') {
|
|
165851
|
+
output.claimPatches = context.config.claimPatches;
|
|
165852
|
+
}
|
|
166590
165853
|
context.config.files.forEach((file) => {
|
|
166591
165854
|
const { src, dest } = file;
|
|
166592
165855
|
// For now let's keep upgradable flag for backward compatibility
|
|
@@ -166607,30 +165870,106 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
|
|
|
166607
165870
|
userManaged: userManaged,
|
|
166608
165871
|
targetBranch,
|
|
166609
165872
|
});
|
|
166610
|
-
output.patches = context.config.patches;
|
|
166611
165873
|
// Not used anymore
|
|
166612
165874
|
// output.unpatches = context.config.unpatches
|
|
166613
165875
|
});
|
|
166614
|
-
output.traceability = context.traceability;
|
|
166615
165876
|
external_fs_default().writeFileSync(external_path_default().join(featureRenderPath, 'config.json'), JSON.stringify(context.config));
|
|
166616
165877
|
// write the output file
|
|
166617
165878
|
const configFile = external_path_default().join(featureRenderPath, 'output.json');
|
|
166618
165879
|
external_fs_default().writeFileSync(configFile, JSON.stringify(output));
|
|
166619
165880
|
return output;
|
|
166620
165881
|
}
|
|
166621
|
-
function
|
|
165882
|
+
function getPatches(featurePath, entity, firestartrConfig = {}, featureArgs = {}) {
|
|
165883
|
+
const configData = validate(featurePath);
|
|
165884
|
+
validateFeatureArgs(featurePath, featureArgs);
|
|
165885
|
+
const context = buildContext(entity, configData.args, firestartrConfig, featureArgs);
|
|
165886
|
+
const rendered = JSON.parse(renderContent(JSON.stringify(configData), context));
|
|
165887
|
+
return rendered.patches ?? [];
|
|
165888
|
+
}
|
|
165889
|
+
function buildContext(stitchedClaim, args, firestartrConfig, featureArgs) {
|
|
166622
165890
|
const context = {};
|
|
165891
|
+
const resolveRef = (doc, ref) => {
|
|
165892
|
+
const v = lodash_default().get(doc, ref);
|
|
165893
|
+
if (v !== undefined)
|
|
165894
|
+
return v;
|
|
165895
|
+
// Fallback for transition: try CR-shaped alternatives when stitchedClaim is CR-shaped
|
|
165896
|
+
// and vice versa. Covers the ADR 0006 table plus legacy feature_a provisioner paths.
|
|
165897
|
+
const altTable = {
|
|
165898
|
+
'providers,github,org': [
|
|
165899
|
+
['spec', 'org'],
|
|
165900
|
+
['spec', 'provisioner', 'org'],
|
|
165901
|
+
['spec', 'firestartr', 'technology', 'stack'],
|
|
165902
|
+
],
|
|
165903
|
+
'providers,github,branchStrategy,defaultBranch': [
|
|
165904
|
+
['spec', 'repo', 'defaultBranch'],
|
|
165905
|
+
['spec', 'provisioner', 'repo', 'defaultBranch'],
|
|
165906
|
+
],
|
|
165907
|
+
'providers,github,name': [
|
|
165908
|
+
['metadata', 'annotations', 'firestartr.dev/external-name'],
|
|
165909
|
+
['metadata', 'name'],
|
|
165910
|
+
],
|
|
165911
|
+
name: [['metadata', 'name']],
|
|
165912
|
+
'annotations,firestartr.dev/dest-annotation': [
|
|
165913
|
+
['metadata', 'annotations', 'firestartr.dev/dest-annotation'],
|
|
165914
|
+
],
|
|
165915
|
+
};
|
|
165916
|
+
const key = ref.join(',');
|
|
165917
|
+
const alts = altTable[key];
|
|
165918
|
+
if (alts) {
|
|
165919
|
+
for (const alt of alts) {
|
|
165920
|
+
const av = lodash_default().get(doc, alt);
|
|
165921
|
+
if (av !== undefined)
|
|
165922
|
+
return av;
|
|
165923
|
+
}
|
|
165924
|
+
}
|
|
165925
|
+
// Reverse fallback: if ref is CR-shaped, try claim-shaped
|
|
165926
|
+
const reverseTable = {
|
|
165927
|
+
'spec,org': ['providers', 'github', 'org'],
|
|
165928
|
+
'spec,provisioner,technology,stack': ['providers', 'github', 'org'],
|
|
165929
|
+
'spec,firestartr,technology,stack': ['providers', 'github', 'org'],
|
|
165930
|
+
'metadata,name': ['name'],
|
|
165931
|
+
'metadata,annotations,firestartr.dev/dest-annotation': [
|
|
165932
|
+
'annotations',
|
|
165933
|
+
'firestartr.dev/dest-annotation',
|
|
165934
|
+
],
|
|
165935
|
+
'spec,provisioner,org': ['providers', 'github', 'org'],
|
|
165936
|
+
'spec,provisioner,repo,defaultBranch': [
|
|
165937
|
+
'providers',
|
|
165938
|
+
'github',
|
|
165939
|
+
'branchStrategy',
|
|
165940
|
+
'defaultBranch',
|
|
165941
|
+
],
|
|
165942
|
+
'spec,repo,defaultBranch': [
|
|
165943
|
+
'providers',
|
|
165944
|
+
'github',
|
|
165945
|
+
'branchStrategy',
|
|
165946
|
+
'defaultBranch',
|
|
165947
|
+
],
|
|
165948
|
+
'metadata,annotations,firestartr.dev/external-name': [
|
|
165949
|
+
'providers',
|
|
165950
|
+
'github',
|
|
165951
|
+
'name',
|
|
165952
|
+
],
|
|
165953
|
+
};
|
|
165954
|
+
const rev = reverseTable[ref.join(',')];
|
|
165955
|
+
if (rev) {
|
|
165956
|
+
const rv = lodash_default().get(doc, rev);
|
|
165957
|
+
if (rv !== undefined)
|
|
165958
|
+
return rv;
|
|
165959
|
+
}
|
|
165960
|
+
return v;
|
|
165961
|
+
};
|
|
166623
165962
|
Object.entries(args).forEach(([key, arg]) => {
|
|
166624
165963
|
if ('$default' in arg) {
|
|
166625
165964
|
if (Array.isArray(arg['$default'])) {
|
|
166626
|
-
context[key] =
|
|
165965
|
+
context[key] = resolveRef(stitchedClaim, arg['$default']);
|
|
166627
165966
|
}
|
|
166628
165967
|
else {
|
|
166629
165968
|
context[key] = arg['$default'];
|
|
166630
165969
|
}
|
|
166631
165970
|
}
|
|
166632
165971
|
if ('$ref' in arg) {
|
|
166633
|
-
const refValue =
|
|
165972
|
+
const refValue = resolveRef(stitchedClaim, arg['$ref']);
|
|
166634
165973
|
context[key] = refValue;
|
|
166635
165974
|
}
|
|
166636
165975
|
else if ('$lit' in arg) {
|
|
@@ -166760,6 +166099,7 @@ function updateFileContent(featureRenderPath, filePath, content) {
|
|
|
166760
166099
|
|
|
166761
166100
|
|
|
166762
166101
|
|
|
166102
|
+
|
|
166763
166103
|
/* harmony default export */ const features_renderer = ({
|
|
166764
166104
|
validate: validate,
|
|
166765
166105
|
render: render,
|
|
@@ -166769,6 +166109,7 @@ function updateFileContent(featureRenderPath, filePath, content) {
|
|
|
166769
166109
|
renderContent: renderContent,
|
|
166770
166110
|
expandFiles: expandFiles,
|
|
166771
166111
|
validateFeatureArgs: validateFeatureArgs,
|
|
166112
|
+
getPatches: getPatches,
|
|
166772
166113
|
});
|
|
166773
166114
|
|
|
166774
166115
|
;// CONCATENATED MODULE: ../features_preparer/src/logger.ts
|
|
@@ -166793,6 +166134,13 @@ function renderFeature(featureName, version, owner, repo, featureOwner, renderPa
|
|
|
166793
166134
|
function renderFeatureFromPath(extractPath, renderedPath, featureOwner, featureArgs = {}) {
|
|
166794
166135
|
return features_renderer.render(extractPath, renderedPath, featureOwner, {}, featureArgs);
|
|
166795
166136
|
}
|
|
166137
|
+
function getFeaturePatches(featureName, version, owner, repo, featureOwner, featureArgs = {}) {
|
|
166138
|
+
const extractPath = external_path_default().join(catalog_common/* default.features.tarballs.getFeaturesExtractPath */.Z.features.tarballs.getFeaturesExtractPath(featureName, version, owner, repo), 'packages', featureName);
|
|
166139
|
+
return getPatches(extractPath, featureOwner, {}, featureArgs);
|
|
166140
|
+
}
|
|
166141
|
+
function getFeaturePatchesFromPath(extractPath, featureOwner, featureArgs = {}) {
|
|
166142
|
+
return getPatches(extractPath, featureOwner, {}, featureArgs);
|
|
166143
|
+
}
|
|
166796
166144
|
|
|
166797
166145
|
// EXTERNAL MODULE: ../../node_modules/adm-zip/adm-zip.js
|
|
166798
166146
|
var adm_zip = __nccwpck_require__(7993);
|
|
@@ -166843,7 +166191,6 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
|
166843
166191
|
async function processFeature(featureName, reference, featureOwner, // -> cr
|
|
166844
166192
|
featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
166845
166193
|
await downloadFeatureZip(repo, featureName, reference, owner);
|
|
166846
|
-
const commitInfo = await resolveFeatureCommit(owner, repo, reference);
|
|
166847
166194
|
featureArgs['traceability'] = {
|
|
166848
166195
|
version: reference,
|
|
166849
166196
|
owner,
|
|
@@ -166851,43 +166198,28 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
|
166851
166198
|
name: featureName,
|
|
166852
166199
|
ref: reference,
|
|
166853
166200
|
url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
|
|
166854
|
-
sha: commitInfo.sha,
|
|
166855
|
-
tags: commitInfo.tags,
|
|
166856
166201
|
};
|
|
166857
166202
|
return renderFeature(featureName, reference, owner, repo, featureOwner, '/tmp', featureArgs);
|
|
166858
166203
|
}
|
|
166859
|
-
async function
|
|
166860
|
-
|
|
166861
|
-
|
|
166862
|
-
|
|
166863
|
-
|
|
166864
|
-
|
|
166865
|
-
|
|
166866
|
-
|
|
166867
|
-
|
|
166868
|
-
|
|
166869
|
-
|
|
166870
|
-
|
|
166871
|
-
|
|
166872
|
-
|
|
166873
|
-
|
|
166874
|
-
|
|
166875
|
-
|
|
166876
|
-
|
|
166877
|
-
|
|
166878
|
-
tags = allTags
|
|
166879
|
-
.filter((t) => t.commit?.sha === sha)
|
|
166880
|
-
.map((t) => t.name);
|
|
166881
|
-
}
|
|
166882
|
-
catch (tagError) {
|
|
166883
|
-
features_preparer_src_logger.warn(`[features_preparer] Failed to list tags for ${owner}/${repo}@${reference}: ${tagError instanceof Error ? tagError.message : String(tagError)}`);
|
|
166884
|
-
}
|
|
166885
|
-
return { sha, tags };
|
|
166886
|
-
}
|
|
166887
|
-
catch (error) {
|
|
166888
|
-
features_preparer_src_logger.warn(`[features_preparer] Failed to resolve commit for ${owner}/${repo}@${reference}: ${error instanceof Error ? error.message : String(error)}`);
|
|
166889
|
-
return { sha: '', tags: [] };
|
|
166890
|
-
}
|
|
166204
|
+
async function installer_getFeaturePatches(featureName, version, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
166205
|
+
const reference = `${featureName}-v${version}`;
|
|
166206
|
+
return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
|
|
166207
|
+
}
|
|
166208
|
+
async function getFeaturePatchesFromRef(featureName, featureRef, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
166209
|
+
const reference = `${featureRef}`;
|
|
166210
|
+
return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
|
|
166211
|
+
}
|
|
166212
|
+
async function processFeaturePatches(featureName, reference, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
|
|
166213
|
+
await downloadFeatureZip(repo, featureName, reference, owner);
|
|
166214
|
+
featureArgs['traceability'] = {
|
|
166215
|
+
version: reference,
|
|
166216
|
+
owner,
|
|
166217
|
+
repo,
|
|
166218
|
+
name: featureName,
|
|
166219
|
+
ref: reference,
|
|
166220
|
+
url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
|
|
166221
|
+
};
|
|
166222
|
+
return getFeaturePatches(featureName, reference, owner, repo, featureOwner, featureArgs);
|
|
166891
166223
|
}
|
|
166892
166224
|
async function prepareFeature(featureName, version, repo = 'features', owner = 'prefapp') {
|
|
166893
166225
|
await downloadFeatureZip(repo, featureName, version, owner);
|
|
@@ -166943,6 +166275,9 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
|
|
|
166943
166275
|
/* harmony default export */ const src = ({
|
|
166944
166276
|
renderFeature: renderFeature,
|
|
166945
166277
|
renderFeatureFromPath: renderFeatureFromPath,
|
|
166278
|
+
getFeaturePatches: installer_getFeaturePatches,
|
|
166279
|
+
getFeaturePatchesFromRef: getFeaturePatchesFromRef,
|
|
166280
|
+
getFeaturePatchesFromPath: getFeaturePatchesFromPath,
|
|
166946
166281
|
getFeatureConfigFromRef: getFeatureConfigFromRef,
|
|
166947
166282
|
getFeatureConfig: getFeatureConfig,
|
|
166948
166283
|
prepareFeature: prepareFeature,
|
|
@@ -166959,6 +166294,1139 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
|
|
|
166959
166294
|
getFeatureConfigFromRef: src.getFeatureConfigFromRef,
|
|
166960
166295
|
});
|
|
166961
166296
|
|
|
166297
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/protectedPaths.ts
|
|
166298
|
+
// Platform-curated protected claim paths — features may not write to these.
|
|
166299
|
+
// Stored as JSON Pointers (RFC 6901). Supports wildcard `*` for a single segment.
|
|
166300
|
+
// Example: /providers/STAR/org matches /providers/github/org where STAR is *.
|
|
166301
|
+
const PROTECTED_CLAIM_PATHS = [
|
|
166302
|
+
'/kind',
|
|
166303
|
+
'/name',
|
|
166304
|
+
'/providers/*/org',
|
|
166305
|
+
'/providers/github/org',
|
|
166306
|
+
'/providers/github/name',
|
|
166307
|
+
'/metadata/name',
|
|
166308
|
+
'/metadata/namespace',
|
|
166309
|
+
];
|
|
166310
|
+
function isProtectedPath(patchPath) {
|
|
166311
|
+
const normalizedPatch = normalizePointer(patchPath);
|
|
166312
|
+
for (const protectedPath of PROTECTED_CLAIM_PATHS) {
|
|
166313
|
+
if (matchesProtected(protectedPath, normalizedPatch)) {
|
|
166314
|
+
return true;
|
|
166315
|
+
}
|
|
166316
|
+
}
|
|
166317
|
+
return false;
|
|
166318
|
+
}
|
|
166319
|
+
function normalizePointer(pointer) {
|
|
166320
|
+
if (!pointer.startsWith('/')) {
|
|
166321
|
+
return '/' + pointer;
|
|
166322
|
+
}
|
|
166323
|
+
return pointer;
|
|
166324
|
+
}
|
|
166325
|
+
function matchesProtected(protectedPointer, patchPointer) {
|
|
166326
|
+
const protectedSegments = protectedPointer
|
|
166327
|
+
.split('/')
|
|
166328
|
+
.slice(1)
|
|
166329
|
+
.map(unescapePointer);
|
|
166330
|
+
const patchSegments = patchPointer.split('/').slice(1).map(unescapePointer);
|
|
166331
|
+
// Treat ancestor/descendant as protected: a patch to a parent (e.g. /metadata)
|
|
166332
|
+
// would replace the protected child (/metadata/name), and a patch to a child
|
|
166333
|
+
// (e.g. /metadata/name/extra) is also under the protected tree. Match when
|
|
166334
|
+
// the shorter prefix equals the longer prefix (with wildcard support).
|
|
166335
|
+
const minLen = Math.min(protectedSegments.length, patchSegments.length);
|
|
166336
|
+
for (let i = 0; i < minLen; i++) {
|
|
166337
|
+
if (protectedSegments[i] === '*')
|
|
166338
|
+
continue;
|
|
166339
|
+
if (protectedSegments[i] !== patchSegments[i])
|
|
166340
|
+
return false;
|
|
166341
|
+
}
|
|
166342
|
+
return true;
|
|
166343
|
+
}
|
|
166344
|
+
function unescapePointer(segment) {
|
|
166345
|
+
return segment.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
166346
|
+
}
|
|
166347
|
+
|
|
166348
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/errors.ts
|
|
166349
|
+
class StitchingError extends Error {
|
|
166350
|
+
constructor(violations) {
|
|
166351
|
+
const lines = violations.map((v) => ` - ${v.message}`).join('\n');
|
|
166352
|
+
super(`Claim stitching failed with ${violations.length} violation(s):\n${lines}`);
|
|
166353
|
+
this.name = 'StitchingError';
|
|
166354
|
+
this.violations = violations;
|
|
166355
|
+
}
|
|
166356
|
+
}
|
|
166357
|
+
function formatStitchingError(error) {
|
|
166358
|
+
return error.message;
|
|
166359
|
+
}
|
|
166360
|
+
|
|
166361
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/stitching.ts
|
|
166362
|
+
|
|
166363
|
+
|
|
166364
|
+
|
|
166365
|
+
|
|
166366
|
+
let MOCK_FEATURES_FN;
|
|
166367
|
+
function MOCK_STITCHING_FEATURES(mock) {
|
|
166368
|
+
MOCK_FEATURES_FN = mock;
|
|
166369
|
+
}
|
|
166370
|
+
function pointerExists(doc, pointer) {
|
|
166371
|
+
const segments = pointer
|
|
166372
|
+
.split('/')
|
|
166373
|
+
.slice(1)
|
|
166374
|
+
.map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
166375
|
+
let cur = doc;
|
|
166376
|
+
for (const seg of segments) {
|
|
166377
|
+
if (cur === null || cur === undefined || typeof cur !== 'object')
|
|
166378
|
+
return false;
|
|
166379
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg))
|
|
166380
|
+
return false;
|
|
166381
|
+
cur = cur[seg];
|
|
166382
|
+
}
|
|
166383
|
+
return true;
|
|
166384
|
+
}
|
|
166385
|
+
function isAppendPointer(pointer) {
|
|
166386
|
+
return pointer.endsWith('/-');
|
|
166387
|
+
}
|
|
166388
|
+
function isRecord(value) {
|
|
166389
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
166390
|
+
}
|
|
166391
|
+
// Deduplication identity for a feature patch. Ordinary writes collide on the
|
|
166392
|
+
// normalized pointer. Array appends (`add ... /-`) collide on the pointer plus
|
|
166393
|
+
// the appended element's `name` when present, for example so several features can each add
|
|
166394
|
+
// a distinct label to /providers/github/labels/- while the same label name from
|
|
166395
|
+
// two features is silently deduplicated (or errors if values differ).
|
|
166396
|
+
function writeIdentity(patch) {
|
|
166397
|
+
const path = normalizePointer(patch.path);
|
|
166398
|
+
if (patch.op === 'add' &&
|
|
166399
|
+
isAppendPointer(path) &&
|
|
166400
|
+
isRecord(patch.value) &&
|
|
166401
|
+
typeof patch.value.name === 'string') {
|
|
166402
|
+
return `${path}#${patch.value.name}`;
|
|
166403
|
+
}
|
|
166404
|
+
return path;
|
|
166405
|
+
}
|
|
166406
|
+
// Check whether an array at `appendPath`'s parent already contains an element
|
|
166407
|
+
// whose `name` matches `value.name`. Used for the "claim wins" gate: when a
|
|
166408
|
+
// feature tries to append an array element that already exists in the claim,
|
|
166409
|
+
// the patch is silently dropped.
|
|
166410
|
+
function arrayElementExists(doc, appendPath, value) {
|
|
166411
|
+
if (!isRecord(value) || typeof value.name !== 'string')
|
|
166412
|
+
return false;
|
|
166413
|
+
const parentPath = appendPath.slice(0, -2); // strip trailing /-
|
|
166414
|
+
const segments = parentPath
|
|
166415
|
+
.split('/')
|
|
166416
|
+
.slice(1)
|
|
166417
|
+
.map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
166418
|
+
let cur = doc;
|
|
166419
|
+
for (const seg of segments) {
|
|
166420
|
+
if (cur === null || cur === undefined || typeof cur !== 'object')
|
|
166421
|
+
return false;
|
|
166422
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg))
|
|
166423
|
+
return false;
|
|
166424
|
+
cur = cur[seg];
|
|
166425
|
+
}
|
|
166426
|
+
if (!Array.isArray(cur))
|
|
166427
|
+
return false;
|
|
166428
|
+
return cur.some((el) => isRecord(el) && el.name === value.name);
|
|
166429
|
+
}
|
|
166430
|
+
// fast-json-patch does not create intermediate containers for `add`, and
|
|
166431
|
+
// appending to an array requires the array to already exist. Ensure every
|
|
166432
|
+
// ancestor segment exists before applying an `add`: missing containers are
|
|
166433
|
+
// created as objects, except the immediate parent of a `/-` append, which is
|
|
166434
|
+
// created as an array. Existing (user/default-declared) values are never
|
|
166435
|
+
// overwritten.
|
|
166436
|
+
function ensureAddParents(doc, patch) {
|
|
166437
|
+
const segments = patch.path.split('/').slice(1);
|
|
166438
|
+
let prefix = '';
|
|
166439
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
166440
|
+
prefix += '/' + segments[i];
|
|
166441
|
+
if (pointerExists(doc, prefix))
|
|
166442
|
+
continue;
|
|
166443
|
+
const isArrayParent = isAppendPointer(patch.path) && i === segments.length - 2;
|
|
166444
|
+
const value = isArrayParent ? [] : {};
|
|
166445
|
+
try {
|
|
166446
|
+
doc = fast_json_patch.applyPatch(doc, [{ op: 'add', path: prefix, value }], false, false).newDocument;
|
|
166447
|
+
}
|
|
166448
|
+
catch {
|
|
166449
|
+
// Concurrent structural conflict — let the actual patch surface the error.
|
|
166450
|
+
}
|
|
166451
|
+
}
|
|
166452
|
+
return doc;
|
|
166453
|
+
}
|
|
166454
|
+
async function stitchClaim(claimAfterDefaults, preCollectedEnvelopes) {
|
|
166455
|
+
const envelopes = preCollectedEnvelopes ?? (await collectFeaturePatches(claimAfterDefaults));
|
|
166456
|
+
if (envelopes.length === 0) {
|
|
166457
|
+
return claimAfterDefaults;
|
|
166458
|
+
}
|
|
166459
|
+
// Normalize all patches once so every gate and the apply phase use the same pointers.
|
|
166460
|
+
const normalizedEnvelopes = envelopes.map((env) => ({
|
|
166461
|
+
feature: env.feature,
|
|
166462
|
+
patches: env.patches.map((p) => ({
|
|
166463
|
+
...p,
|
|
166464
|
+
path: normalizePointer(p.path),
|
|
166465
|
+
_raw: p.path,
|
|
166466
|
+
})),
|
|
166467
|
+
}));
|
|
166468
|
+
// ── Phase 1: silently drop patches that should not be applied ──────────
|
|
166469
|
+
// • Array append whose element already exists in the claim → claim wins
|
|
166470
|
+
// • Duplicate identity with identical value across features → silent dedup
|
|
166471
|
+
const dropped = new Set(); // "envelopeIdx:patchIdx"
|
|
166472
|
+
const seenIdentities = new Map();
|
|
166473
|
+
for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
|
|
166474
|
+
const envelope = normalizedEnvelopes[ei];
|
|
166475
|
+
for (let pi = 0; pi < envelope.patches.length; pi++) {
|
|
166476
|
+
const patch = envelope.patches[pi];
|
|
166477
|
+
const key = `${ei}:${pi}`;
|
|
166478
|
+
// Array claim-wins: element already in claim → drop
|
|
166479
|
+
if (patch.op === 'add' && isAppendPointer(patch.path)) {
|
|
166480
|
+
if (arrayElementExists(claimAfterDefaults, patch.path, patch.value)) {
|
|
166481
|
+
dropped.add(key);
|
|
166482
|
+
continue;
|
|
166483
|
+
}
|
|
166484
|
+
}
|
|
166485
|
+
// Silent dedup: same identity + same value → drop
|
|
166486
|
+
const identity = writeIdentity(patch);
|
|
166487
|
+
const existing = seenIdentities.get(identity);
|
|
166488
|
+
if (existing) {
|
|
166489
|
+
if (JSON.stringify(existing.value) === JSON.stringify(patch.value)) {
|
|
166490
|
+
dropped.add(key);
|
|
166491
|
+
continue;
|
|
166492
|
+
}
|
|
166493
|
+
// Different value — kept here, caught as a violation in Phase 2.
|
|
166494
|
+
}
|
|
166495
|
+
else {
|
|
166496
|
+
seenIdentities.set(identity, {
|
|
166497
|
+
feature: envelope.feature,
|
|
166498
|
+
value: patch.value,
|
|
166499
|
+
});
|
|
166500
|
+
}
|
|
166501
|
+
}
|
|
166502
|
+
}
|
|
166503
|
+
// ── Phase 2: collect violations on non-dropped patches ─────────────────
|
|
166504
|
+
const violations = [];
|
|
166505
|
+
const seenForViolations = new Map(); // identity → feature
|
|
166506
|
+
for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
|
|
166507
|
+
const envelope = normalizedEnvelopes[ei];
|
|
166508
|
+
for (let pi = 0; pi < envelope.patches.length; pi++) {
|
|
166509
|
+
if (dropped.has(`${ei}:${pi}`))
|
|
166510
|
+
continue;
|
|
166511
|
+
const patch = envelope.patches[pi];
|
|
166512
|
+
const path = patch.path;
|
|
166513
|
+
const raw = patch._raw ?? path;
|
|
166514
|
+
// Gate 1: protected path
|
|
166515
|
+
if (isProtectedPath(path)) {
|
|
166516
|
+
violations.push({
|
|
166517
|
+
feature: envelope.feature,
|
|
166518
|
+
op: patch.op,
|
|
166519
|
+
path,
|
|
166520
|
+
reason: 'protected-path',
|
|
166521
|
+
message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets protected claim path`,
|
|
166522
|
+
});
|
|
166523
|
+
}
|
|
166524
|
+
// Gate 2: conflicting value from another feature
|
|
166525
|
+
const identity = writeIdentity(patch);
|
|
166526
|
+
if (seenForViolations.has(identity)) {
|
|
166527
|
+
violations.push({
|
|
166528
|
+
feature: envelope.feature,
|
|
166529
|
+
op: patch.op,
|
|
166530
|
+
path,
|
|
166531
|
+
reason: 'duplicate-path',
|
|
166532
|
+
conflictingFeature: seenForViolations.get(identity),
|
|
166533
|
+
message: `Feature '${envelope.feature}' and '${seenForViolations.get(identity)}' both write to ${path} (deduplication)`,
|
|
166534
|
+
});
|
|
166535
|
+
}
|
|
166536
|
+
else {
|
|
166537
|
+
seenForViolations.set(identity, envelope.feature);
|
|
166538
|
+
}
|
|
166539
|
+
// Gate 3: overwrite protection — features may only add to absent paths
|
|
166540
|
+
// and must never overwrite a value the user (or claims_defaults.yaml)
|
|
166541
|
+
// declared (#2648).
|
|
166542
|
+
const targetExists = pointerExists(claimAfterDefaults, path);
|
|
166543
|
+
const isArrayAppend = patch.op === 'add' && isAppendPointer(path);
|
|
166544
|
+
if (patch.op === 'add') {
|
|
166545
|
+
// `add` on an existing, non-append path is an overwrite in JSON Patch,
|
|
166546
|
+
// so reject it. Array appends (`... /-`) add a new element; a genuinely
|
|
166547
|
+
// new element targets an absent slot (claim-wins/dedup handled elsewhere)
|
|
166548
|
+
// so those are allowed.
|
|
166549
|
+
if (!isArrayAppend && targetExists) {
|
|
166550
|
+
violations.push({
|
|
166551
|
+
feature: envelope.feature,
|
|
166552
|
+
op: patch.op,
|
|
166553
|
+
path,
|
|
166554
|
+
reason: 'overwrite-protected',
|
|
166555
|
+
message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets existing path (features may only add to absent paths, never overwrite user-declared values)`,
|
|
166556
|
+
});
|
|
166557
|
+
}
|
|
166558
|
+
}
|
|
166559
|
+
else if (['replace', 'remove'].includes(patch.op)) {
|
|
166560
|
+
// replace/remove on an existing path overwrites user intent; on an
|
|
166561
|
+
// absent path fast-json-patch would fail anyway. Either way: reject.
|
|
166562
|
+
violations.push({
|
|
166563
|
+
feature: envelope.feature,
|
|
166564
|
+
op: patch.op,
|
|
166565
|
+
path,
|
|
166566
|
+
reason: 'overwrite-protected',
|
|
166567
|
+
message: targetExists
|
|
166568
|
+
? `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets existing path (cannot overwrite user-declared value)`
|
|
166569
|
+
: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets absent path (replace/remove requires existing value)`,
|
|
166570
|
+
});
|
|
166571
|
+
}
|
|
166572
|
+
}
|
|
166573
|
+
}
|
|
166574
|
+
if (violations.length > 0) {
|
|
166575
|
+
throw new StitchingError(violations);
|
|
166576
|
+
}
|
|
166577
|
+
// ── Phase 3: apply remaining patches ───────────────────────────────────
|
|
166578
|
+
let stitched = JSON.parse(JSON.stringify(claimAfterDefaults));
|
|
166579
|
+
for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
|
|
166580
|
+
const envelope = normalizedEnvelopes[ei];
|
|
166581
|
+
const patchesToApply = envelope.patches.filter((_, pi) => !dropped.has(`${ei}:${pi}`));
|
|
166582
|
+
if (patchesToApply.length === 0)
|
|
166583
|
+
continue;
|
|
166584
|
+
for (const patch of patchesToApply) {
|
|
166585
|
+
if (patch.op === 'add') {
|
|
166586
|
+
stitched = ensureAddParents(stitched, patch);
|
|
166587
|
+
}
|
|
166588
|
+
}
|
|
166589
|
+
const result = fast_json_patch.applyPatch(stitched, patchesToApply, false, false);
|
|
166590
|
+
stitched = result.newDocument;
|
|
166591
|
+
}
|
|
166592
|
+
return stitched;
|
|
166593
|
+
}
|
|
166594
|
+
async function collectFeaturePatches(claim) {
|
|
166595
|
+
if (MOCK_FEATURES_FN) {
|
|
166596
|
+
return MOCK_FEATURES_FN(claim);
|
|
166597
|
+
}
|
|
166598
|
+
const typedClaim = claim;
|
|
166599
|
+
const features = typedClaim.providers?.github?.features ?? [];
|
|
166600
|
+
if (features.length === 0)
|
|
166601
|
+
return [];
|
|
166602
|
+
// In unit tests without GitHub credentials, avoid real download — tests that care
|
|
166603
|
+
// about stitching should set MOCK_STITCHING_FEATURES explicitly.
|
|
166604
|
+
if (process.env.JEST_WORKER_ID && !process.env.GITHUB_TOKEN) {
|
|
166605
|
+
return [];
|
|
166606
|
+
}
|
|
166607
|
+
const envelopes = [];
|
|
166608
|
+
for (const feature of features) {
|
|
166609
|
+
const featureName = feature.name;
|
|
166610
|
+
const versionOrRef = feature.ref ?? feature.version ?? '';
|
|
166611
|
+
const repo = feature.repo
|
|
166612
|
+
? (feature.repo.split('/')[1] ?? 'features')
|
|
166613
|
+
: 'features';
|
|
166614
|
+
const owner = feature.repo
|
|
166615
|
+
? (feature.repo.split('/')[0] ?? 'prefapp')
|
|
166616
|
+
: 'prefapp';
|
|
166617
|
+
const args = feature.args ?? {};
|
|
166618
|
+
try {
|
|
166619
|
+
let patchesOrRendered;
|
|
166620
|
+
const preparerAny = features_preparer;
|
|
166621
|
+
if (feature.ref) {
|
|
166622
|
+
if (typeof preparerAny.getFeaturePatchesFromRef === 'function') {
|
|
166623
|
+
patchesOrRendered = await preparerAny.getFeaturePatchesFromRef(featureName, versionOrRef, claim, args, repo, owner);
|
|
166624
|
+
}
|
|
166625
|
+
else {
|
|
166626
|
+
patchesOrRendered = await preparerAny.getFeatureConfigFromRef(featureName, versionOrRef, claim, args, repo, owner);
|
|
166627
|
+
}
|
|
166628
|
+
}
|
|
166629
|
+
else {
|
|
166630
|
+
if (typeof preparerAny.getFeaturePatches === 'function') {
|
|
166631
|
+
patchesOrRendered = await preparerAny.getFeaturePatches(featureName, versionOrRef, claim, args, repo, owner);
|
|
166632
|
+
}
|
|
166633
|
+
else {
|
|
166634
|
+
patchesOrRendered = await preparerAny.getFeatureConfig(featureName, versionOrRef, claim, args, repo, owner);
|
|
166635
|
+
}
|
|
166636
|
+
}
|
|
166637
|
+
const patches = Array.isArray(patchesOrRendered)
|
|
166638
|
+
? patchesOrRendered
|
|
166639
|
+
: extractClaimPatches(patchesOrRendered);
|
|
166640
|
+
envelopes.push({ feature: featureName, patches });
|
|
166641
|
+
}
|
|
166642
|
+
catch (err) {
|
|
166643
|
+
throw new Error(`Failed to collect patches for feature '${featureName}': ${err instanceof Error ? err.message : String(err)}`);
|
|
166644
|
+
}
|
|
166645
|
+
}
|
|
166646
|
+
return envelopes;
|
|
166647
|
+
}
|
|
166648
|
+
function extractClaimPatches(rendered) {
|
|
166649
|
+
const patches = rendered.patches;
|
|
166650
|
+
if (!patches)
|
|
166651
|
+
return [];
|
|
166652
|
+
if (Array.isArray(patches))
|
|
166653
|
+
return patches;
|
|
166654
|
+
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`);
|
|
166655
|
+
}
|
|
166656
|
+
|
|
166657
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
|
|
166658
|
+
|
|
166659
|
+
|
|
166660
|
+
|
|
166661
|
+
|
|
166662
|
+
|
|
166663
|
+
|
|
166664
|
+
|
|
166665
|
+
|
|
166666
|
+
|
|
166667
|
+
|
|
166668
|
+
|
|
166669
|
+
|
|
166670
|
+
|
|
166671
|
+
|
|
166672
|
+
|
|
166673
|
+
|
|
166674
|
+
|
|
166675
|
+
// We need to ensure the grep command is a gnu version
|
|
166676
|
+
let IS_GNU_GREP = false;
|
|
166677
|
+
let GREP_ON_CHECK = false;
|
|
166678
|
+
// this loop is for avoid race conditions
|
|
166679
|
+
// once a check is running no more checks
|
|
166680
|
+
async function waitForCheck() {
|
|
166681
|
+
const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
|
|
166682
|
+
do {
|
|
166683
|
+
await f_wait();
|
|
166684
|
+
} while (GREP_ON_CHECK);
|
|
166685
|
+
}
|
|
166686
|
+
function checkGrep() {
|
|
166687
|
+
return new Promise((ok, ko) => {
|
|
166688
|
+
// idempotency: already checked
|
|
166689
|
+
if (IS_GNU_GREP)
|
|
166690
|
+
return ok();
|
|
166691
|
+
// a check is already running no new checks
|
|
166692
|
+
if (GREP_ON_CHECK)
|
|
166693
|
+
return waitForCheck();
|
|
166694
|
+
logger.info('Checking the grep command');
|
|
166695
|
+
GREP_ON_CHECK = true;
|
|
166696
|
+
const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
|
|
166697
|
+
handler.on('close', (code) => {
|
|
166698
|
+
GREP_ON_CHECK = false;
|
|
166699
|
+
if (code === 0) {
|
|
166700
|
+
logger.debug('Grep is a gnu grep');
|
|
166701
|
+
IS_GNU_GREP = true;
|
|
166702
|
+
ok();
|
|
166703
|
+
}
|
|
166704
|
+
else {
|
|
166705
|
+
ko('Lazy evaluation needs a gnu-grep command');
|
|
166706
|
+
}
|
|
166707
|
+
});
|
|
166708
|
+
});
|
|
166709
|
+
}
|
|
166710
|
+
async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
|
|
166711
|
+
await checkGrep();
|
|
166712
|
+
let result = existingRefs;
|
|
166713
|
+
logger.info(`Load reference ${claimRef}`);
|
|
166714
|
+
initVirtualClaims(org);
|
|
166715
|
+
logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
|
|
166716
|
+
// cargas datos con grep
|
|
166717
|
+
try {
|
|
166718
|
+
const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
|
|
166719
|
+
let claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
|
|
166720
|
+
// Claim stitching: feature claim-patches with three gates before AJV
|
|
166721
|
+
try {
|
|
166722
|
+
claim = await stitchClaim(claim);
|
|
166723
|
+
}
|
|
166724
|
+
catch (e) {
|
|
166725
|
+
throw new Error(`Error when stitching claim ${claimRef}: ${e instanceof Error ? e.message : String(e)}`);
|
|
166726
|
+
}
|
|
166727
|
+
let variants = [];
|
|
166728
|
+
if (claim.kind === 'TFWorkspaceClaim' &&
|
|
166729
|
+
claim.providers?.terraform?.variants) {
|
|
166730
|
+
variants = claim.providers.terraform.variants;
|
|
166731
|
+
delete claim.providers.terraform.variants;
|
|
166732
|
+
}
|
|
166733
|
+
logger.silly(`Patched claim is:
|
|
166734
|
+
---
|
|
166735
|
+
${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
|
|
166736
|
+
try {
|
|
166737
|
+
validateClaim(claim, base[`${claim.kind}Schema`]);
|
|
166738
|
+
await optionalValidation(claim);
|
|
166739
|
+
}
|
|
166740
|
+
catch (error) {
|
|
166741
|
+
let errorMsg = '';
|
|
166742
|
+
if (Array.isArray(error)) {
|
|
166743
|
+
for (const data of error) {
|
|
166744
|
+
errorMsg = `${errorMsg}
|
|
166745
|
+
- ${data.instancePath}: ${data.message}`;
|
|
166746
|
+
}
|
|
166747
|
+
}
|
|
166748
|
+
else {
|
|
166749
|
+
errorMsg = error instanceof Error ? error.message : String(error);
|
|
166750
|
+
}
|
|
166751
|
+
throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
|
|
166752
|
+
}
|
|
166753
|
+
result[claimRef] = {};
|
|
166754
|
+
result[claimRef]['claim'] = claim;
|
|
166755
|
+
result[claimRef]['claimPath'] = VisitedClaims[claimRef];
|
|
166756
|
+
if (VisitedClaims[claimRef] === 'virtual') {
|
|
166757
|
+
result = await setVirtualClaimAdditionalData(result, claim, claimRef);
|
|
166758
|
+
}
|
|
166759
|
+
else {
|
|
166760
|
+
result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
|
|
166761
|
+
}
|
|
166762
|
+
const claimKind = claim.kind;
|
|
166763
|
+
const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
|
|
166764
|
+
for (const ref of references) {
|
|
166765
|
+
if (!result[ref]) {
|
|
166766
|
+
const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
|
|
166767
|
+
result = lodash_default().merge(result, resolvedReferences);
|
|
166768
|
+
}
|
|
166769
|
+
}
|
|
166770
|
+
if (variants.length > 0) {
|
|
166771
|
+
const parentClaimPath = VisitedClaims[claimRef];
|
|
166772
|
+
for (const variant of variants) {
|
|
166773
|
+
if (!variant.name || !variant.overrides) {
|
|
166774
|
+
throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
|
|
166775
|
+
}
|
|
166776
|
+
const prohibitedOverrideFields = ['source', 'module', 'name'];
|
|
166777
|
+
for (const field of prohibitedOverrideFields) {
|
|
166778
|
+
if (field in variant.overrides) {
|
|
166779
|
+
throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
|
|
166780
|
+
}
|
|
166781
|
+
}
|
|
166782
|
+
const composedName = claim.providers.terraform.name + '-' + variant.name;
|
|
166783
|
+
const variantClaim = lodash_default().cloneDeep(claim);
|
|
166784
|
+
variantClaim._parentClaimName = claim.name;
|
|
166785
|
+
variantClaim.name = composedName;
|
|
166786
|
+
variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
|
|
166787
|
+
variantClaim.providers.terraform.name = composedName;
|
|
166788
|
+
if (!variantClaim.annotations) {
|
|
166789
|
+
variantClaim.annotations = {};
|
|
166790
|
+
}
|
|
166791
|
+
variantClaim.annotations['firestartr.dev/variant-of'] =
|
|
166792
|
+
claim.providers.terraform.name;
|
|
166793
|
+
const variantRef = `TFWorkspaceClaim-${composedName}`;
|
|
166794
|
+
if (result[variantRef]) {
|
|
166795
|
+
throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
|
|
166796
|
+
}
|
|
166797
|
+
logger.info(`Creating synthetic variant claim ${variantRef}`);
|
|
166798
|
+
result[variantRef] = {};
|
|
166799
|
+
result[variantRef]['claim'] = variantClaim;
|
|
166800
|
+
result[variantRef]['claimPath'] = parentClaimPath;
|
|
166801
|
+
result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
|
|
166802
|
+
const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
|
|
166803
|
+
for (const ref of variantReferences) {
|
|
166804
|
+
if (!result[ref]) {
|
|
166805
|
+
const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
|
|
166806
|
+
result = lodash_default().merge(result, resolvedReferences);
|
|
166807
|
+
}
|
|
166808
|
+
}
|
|
166809
|
+
}
|
|
166810
|
+
}
|
|
166811
|
+
}
|
|
166812
|
+
catch (err) {
|
|
166813
|
+
throw `Lazy Loading: ${err}`;
|
|
166814
|
+
}
|
|
166815
|
+
return [result, postValidations];
|
|
166816
|
+
}
|
|
166817
|
+
let LoadedClaims = {};
|
|
166818
|
+
let VisitedClaims = {};
|
|
166819
|
+
let DuplicatedClaims = {};
|
|
166820
|
+
function resetLazyLoader() {
|
|
166821
|
+
LoadedClaims = {};
|
|
166822
|
+
VisitedClaims = {};
|
|
166823
|
+
DuplicatedClaims = {};
|
|
166824
|
+
}
|
|
166825
|
+
async function lazyGetClaim(kind, name, org, cwd) {
|
|
166826
|
+
const indice = `${kind}-${name}`;
|
|
166827
|
+
logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
|
|
166828
|
+
if (indice in LoadedClaims)
|
|
166829
|
+
return LoadedClaims[indice];
|
|
166830
|
+
if (isVirtualClaim(kind, name)) {
|
|
166831
|
+
await loadVirtualClaim(kind, name, org);
|
|
166832
|
+
}
|
|
166833
|
+
else {
|
|
166834
|
+
await getClaimsByName(name, cwd);
|
|
166835
|
+
}
|
|
166836
|
+
if (indice in LoadedClaims) {
|
|
166837
|
+
return LoadedClaims[indice];
|
|
166838
|
+
}
|
|
166839
|
+
else {
|
|
166840
|
+
throw new Error(`Error: ${kind}-${name} not found`);
|
|
166841
|
+
}
|
|
166842
|
+
}
|
|
166843
|
+
async function getClaimsByName(name, cwd = '.') {
|
|
166844
|
+
return new Promise((ok, ko) => {
|
|
166845
|
+
const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
|
|
166846
|
+
cwd: cwd,
|
|
166847
|
+
});
|
|
166848
|
+
logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
|
|
166849
|
+
const entradas = [];
|
|
166850
|
+
const chunks = [];
|
|
166851
|
+
handler.stdout.on('data', (data) => {
|
|
166852
|
+
chunks.push(data.toString('utf-8'));
|
|
166853
|
+
});
|
|
166854
|
+
handler.on('close', () => {
|
|
166855
|
+
const dataFinal = chunks.join('');
|
|
166856
|
+
const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
|
|
166857
|
+
for (const fileName of fileNameList) {
|
|
166858
|
+
// only yaml files
|
|
166859
|
+
if (!fileName.match(/\.yml$|\.yaml$/)) {
|
|
166860
|
+
continue;
|
|
166861
|
+
}
|
|
166862
|
+
// only entries not already visited
|
|
166863
|
+
if (Object.values(VisitedClaims).includes(fileName)) {
|
|
166864
|
+
continue;
|
|
166865
|
+
}
|
|
166866
|
+
entradas.push(external_path_.join(cwd, fileName));
|
|
166867
|
+
}
|
|
166868
|
+
return Promise.all(entradas.map((entrada) => {
|
|
166869
|
+
return loadRawClaim(entrada);
|
|
166870
|
+
}))
|
|
166871
|
+
.then(() => {
|
|
166872
|
+
ok();
|
|
166873
|
+
})
|
|
166874
|
+
.catch((err) => {
|
|
166875
|
+
ko(err);
|
|
166876
|
+
});
|
|
166877
|
+
});
|
|
166878
|
+
});
|
|
166879
|
+
}
|
|
166880
|
+
async function loadRawClaim(entry) {
|
|
166881
|
+
return new Promise((ok, ko) => {
|
|
166882
|
+
external_fs_.readFile(entry, 'utf-8', (error, data) => {
|
|
166883
|
+
if (error)
|
|
166884
|
+
return ko(`Reading ${entry}: ${error}`);
|
|
166885
|
+
const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
166886
|
+
if (!('kind' in claim && 'name' in claim)) {
|
|
166887
|
+
logger.warn(`Invalid claim file ${entry}`);
|
|
166888
|
+
}
|
|
166889
|
+
else {
|
|
166890
|
+
const claimKey = `${claim.kind}-${claim.name}`;
|
|
166891
|
+
if (Object.keys(DuplicatedClaims).includes(claimKey) &&
|
|
166892
|
+
DuplicatedClaims[claimKey] !== entry) {
|
|
166893
|
+
return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
|
|
166894
|
+
}
|
|
166895
|
+
LoadedClaims[claimKey] = data;
|
|
166896
|
+
VisitedClaims[claimKey] = entry;
|
|
166897
|
+
DuplicatedClaims[claimKey] = entry;
|
|
166898
|
+
ok();
|
|
166899
|
+
}
|
|
166900
|
+
});
|
|
166901
|
+
});
|
|
166902
|
+
}
|
|
166903
|
+
async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
|
|
166904
|
+
const virtualClaim = getVirtualClaim(claim.kind, claim.name);
|
|
166905
|
+
renderedData[claimRef]['initializers'] = [
|
|
166906
|
+
new UUIDInitializer(claim),
|
|
166907
|
+
new InitializerClaimRef(),
|
|
166908
|
+
new InitializerDefault(virtualClaim.getDefaultConfig()),
|
|
166909
|
+
];
|
|
166910
|
+
renderedData[claimRef]['globals'] = [];
|
|
166911
|
+
renderedData[claimRef]['overrides'] = [];
|
|
166912
|
+
renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
|
|
166913
|
+
return renderedData;
|
|
166914
|
+
}
|
|
166915
|
+
async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
|
|
166916
|
+
renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
|
|
166917
|
+
renderedData[claimRef]['globals'] = await loadGlobals(claim);
|
|
166918
|
+
renderedData[claimRef]['overrides'] = loadOverrides(claim);
|
|
166919
|
+
renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
|
|
166920
|
+
return renderedData;
|
|
166921
|
+
}
|
|
166922
|
+
async function loadVirtualClaim(kind, name, org) {
|
|
166923
|
+
const virtualClaim = getVirtualClaim(kind, name);
|
|
166924
|
+
const expandedClaim = await virtualClaim.expand({ org });
|
|
166925
|
+
logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
|
|
166926
|
+
LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
|
|
166927
|
+
VisitedClaims[`${kind}-${name}`] = 'virtual';
|
|
166928
|
+
}
|
|
166929
|
+
|
|
166930
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
|
|
166931
|
+
|
|
166932
|
+
|
|
166933
|
+
|
|
166934
|
+
/*
|
|
166935
|
+
* Default blocks are objects that cannot be merged but applied as a whole. Meaning:
|
|
166936
|
+
* - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
|
|
166937
|
+
* - If the block is missing at the claim-level, the default block is applied
|
|
166938
|
+
*/
|
|
166939
|
+
const defaultBlocksPaths = ['/providers/terraform/sync'];
|
|
166940
|
+
function applyBlockAwareDefaults(claim, defaultClaim) {
|
|
166941
|
+
// we always work with a clone of the original
|
|
166942
|
+
const claimClone = JSON.parse(JSON.stringify(claim));
|
|
166943
|
+
// if the block is claim-level defined we store it here
|
|
166944
|
+
const defaultBlocks = new Map();
|
|
166945
|
+
// we remove the blocks entirely of the claim
|
|
166946
|
+
// to achieve an add copy
|
|
166947
|
+
for (const defaultBlockPath of defaultBlocksPaths) {
|
|
166948
|
+
if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
|
|
166949
|
+
continue;
|
|
166950
|
+
const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
|
|
166951
|
+
if (originalValue) {
|
|
166952
|
+
logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
|
|
166953
|
+
defaultBlocks.set(defaultBlockPath, originalValue);
|
|
166954
|
+
fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
|
|
166955
|
+
}
|
|
166956
|
+
}
|
|
166957
|
+
// if the patch has the same path of a block
|
|
166958
|
+
// and the block was defined at claim-level
|
|
166959
|
+
// we interchange the value with the original value
|
|
166960
|
+
// otherwise we use the default
|
|
166961
|
+
const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
|
|
166962
|
+
.filter((jp) => jp.op === 'add')
|
|
166963
|
+
.map((jp) => {
|
|
166964
|
+
if (defaultBlocks.has(jp.path)) {
|
|
166965
|
+
return {
|
|
166966
|
+
...jp,
|
|
166967
|
+
value: defaultBlocks.get(jp.path),
|
|
166968
|
+
};
|
|
166969
|
+
}
|
|
166970
|
+
else {
|
|
166971
|
+
return jp;
|
|
166972
|
+
}
|
|
166973
|
+
});
|
|
166974
|
+
return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
|
|
166975
|
+
}
|
|
166976
|
+
function claimsDefaulter_hasDeepPath(data, deepPath) {
|
|
166977
|
+
return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
|
|
166978
|
+
}
|
|
166979
|
+
|
|
166980
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
|
|
166981
|
+
|
|
166982
|
+
|
|
166983
|
+
|
|
166984
|
+
|
|
166985
|
+
|
|
166986
|
+
|
|
166987
|
+
|
|
166988
|
+
|
|
166989
|
+
|
|
166990
|
+
|
|
166991
|
+
|
|
166992
|
+
|
|
166993
|
+
|
|
166994
|
+
|
|
166995
|
+
|
|
166996
|
+
const isYamlFile = new RegExp(/\.(yaml|yml)$/);
|
|
166997
|
+
/**
|
|
166998
|
+
*
|
|
166999
|
+
* @returns Virtual Claims
|
|
167000
|
+
*/
|
|
167001
|
+
const virtualClaims = [new FirestartrAllClaim()];
|
|
167002
|
+
/*
|
|
167003
|
+
* Loads all applicable globals for a given claim and stores them into a list.
|
|
167004
|
+
*
|
|
167005
|
+
* Input:
|
|
167006
|
+
* - claim: our custom claim object, with which we check if the global is applicable
|
|
167007
|
+
*
|
|
167008
|
+
* Returns:
|
|
167009
|
+
* - A GlobalSection array promise, containing all the globals loaded for this claim
|
|
167010
|
+
*
|
|
167011
|
+
*/
|
|
167012
|
+
async function loadGlobals(claim) {
|
|
167013
|
+
const result = [];
|
|
167014
|
+
await crawl(getPath('globals'), (entry) => {
|
|
167015
|
+
return isYamlFile.test(entry);
|
|
167016
|
+
}, async (entry, data) => {
|
|
167017
|
+
const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
167018
|
+
const ext = external_path_.extname(entry);
|
|
167019
|
+
const name = external_path_.basename(entry, ext);
|
|
167020
|
+
if (name in SECTIONS_BY_FILE_NAME) {
|
|
167021
|
+
const section = new SECTIONS_BY_FILE_NAME[name](globalData);
|
|
167022
|
+
if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
|
|
167023
|
+
throw new Error(`Invalid ${name} global file`);
|
|
167024
|
+
}
|
|
167025
|
+
if (globalData.kind === claim.kind) {
|
|
167026
|
+
result.push(section);
|
|
167027
|
+
}
|
|
167028
|
+
else {
|
|
167029
|
+
logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
|
|
167030
|
+
}
|
|
167031
|
+
}
|
|
167032
|
+
});
|
|
167033
|
+
logger.info(`Loaded globals ${result}`);
|
|
167034
|
+
return result;
|
|
167035
|
+
}
|
|
167036
|
+
/*
|
|
167037
|
+
* If the input claim is a GithubRepositoryClaim, it loads all it's applicable
|
|
167038
|
+
* overrides and stores them into a list. Otherwise, it returns an empty list.
|
|
167039
|
+
*
|
|
167040
|
+
* Input:
|
|
167041
|
+
* - claim: our custom claim object, which should be a GithubRepositoryClaim
|
|
167042
|
+
*
|
|
167043
|
+
* Returns:
|
|
167044
|
+
* - An Overrider array, with a GithubRepositoryOverrider if the claim
|
|
167045
|
+
* was a GithubRepositoryClaim, or empty if it was not.
|
|
167046
|
+
*
|
|
167047
|
+
*/
|
|
167048
|
+
function loadOverrides(claim) {
|
|
167049
|
+
const overrides = [];
|
|
167050
|
+
if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
|
|
167051
|
+
overrides.push(new GithubRepositoryOverrider());
|
|
167052
|
+
}
|
|
167053
|
+
return overrides;
|
|
167054
|
+
}
|
|
167055
|
+
/**
|
|
167056
|
+
* Load all normalizers and stores them into a list.
|
|
167057
|
+
* @param claim
|
|
167058
|
+
* @returns
|
|
167059
|
+
*/
|
|
167060
|
+
async function loadNormalizers(_claim, path) {
|
|
167061
|
+
const result = [];
|
|
167062
|
+
for (const normalizer of NORMALIZERS) {
|
|
167063
|
+
result.push(new normalizer({ path: path }));
|
|
167064
|
+
}
|
|
167065
|
+
return result;
|
|
167066
|
+
}
|
|
167067
|
+
/*
|
|
167068
|
+
* Loads all applicable initializers for a given claim and stores them into a list.
|
|
167069
|
+
*
|
|
167070
|
+
* Input:
|
|
167071
|
+
* - claim: our custom claim object, with which we check if the initializer is applicable
|
|
167072
|
+
*
|
|
167073
|
+
* Returns:
|
|
167074
|
+
* - An Initializer array promise, containing all the initializers loaded for this claim
|
|
167075
|
+
*
|
|
167076
|
+
*/
|
|
167077
|
+
let loadedInitializers = false;
|
|
167078
|
+
async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
|
|
167079
|
+
const result = [];
|
|
167080
|
+
await crawl(initializersPath, (entry) => {
|
|
167081
|
+
return isYamlFile.test(entry);
|
|
167082
|
+
}, async (entry, data) => {
|
|
167083
|
+
const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
|
|
167084
|
+
const ext = external_path_.extname(entry);
|
|
167085
|
+
const name = external_path_.basename(entry, ext);
|
|
167086
|
+
if (name in INITIALIZERS_BY_FILE_NAME) {
|
|
167087
|
+
const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
|
|
167088
|
+
if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
|
|
167089
|
+
throw new Error(`Invalid ${name} initializer file`);
|
|
167090
|
+
}
|
|
167091
|
+
if (initializerData.kind === claim.kind) {
|
|
167092
|
+
result.push(initializerBfN);
|
|
167093
|
+
}
|
|
167094
|
+
else {
|
|
167095
|
+
logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
|
|
167096
|
+
}
|
|
167097
|
+
}
|
|
167098
|
+
});
|
|
167099
|
+
for (const initializer of INITIALIZERS) {
|
|
167100
|
+
if (initializer.applicableKinds.includes(claim.kind)) {
|
|
167101
|
+
result.push(new initializer({ path: claimPath }));
|
|
167102
|
+
}
|
|
167103
|
+
}
|
|
167104
|
+
logger.info(`Loaded initializers ${JSON.stringify(result)}`);
|
|
167105
|
+
loadedInitializers = result;
|
|
167106
|
+
return loadedInitializers;
|
|
167107
|
+
}
|
|
167108
|
+
function loadClaimDefaults() {
|
|
167109
|
+
try {
|
|
167110
|
+
return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
|
|
167111
|
+
}
|
|
167112
|
+
catch (error) {
|
|
167113
|
+
const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
|
|
167114
|
+
logger.error(errMsg);
|
|
167115
|
+
throw new Error(errMsg);
|
|
167116
|
+
}
|
|
167117
|
+
}
|
|
167118
|
+
/*
|
|
167119
|
+
* Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
|
|
167120
|
+
*
|
|
167121
|
+
* Input:
|
|
167122
|
+
* - claim: our custom claim object, which we want to patch
|
|
167123
|
+
* - defaultsClaims: an object, containing the defaults for each claim kind,
|
|
167124
|
+
* with the same structure that we use for the actual claims
|
|
167125
|
+
*
|
|
167126
|
+
* Returns:
|
|
167127
|
+
* - The patched claim
|
|
167128
|
+
*
|
|
167129
|
+
*/
|
|
167130
|
+
function loader_patchClaim(claim, defaultsClaims) {
|
|
167131
|
+
if (defaultsClaims[claim.kind]) {
|
|
167132
|
+
claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
|
|
167133
|
+
}
|
|
167134
|
+
return claim;
|
|
167135
|
+
}
|
|
167136
|
+
async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
|
|
167137
|
+
const result = {};
|
|
167138
|
+
const fullResult = {};
|
|
167139
|
+
await crawlWithExclusions(crsPath, (entry) => {
|
|
167140
|
+
return isYamlFile.test(entry);
|
|
167141
|
+
}, async (entry, data) => {
|
|
167142
|
+
const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
|
|
167143
|
+
const metadata = yamlData?.metadata;
|
|
167144
|
+
if (!(yamlData &&
|
|
167145
|
+
typeof yamlData === 'object' &&
|
|
167146
|
+
'kind' in yamlData &&
|
|
167147
|
+
metadata &&
|
|
167148
|
+
typeof metadata === 'object' &&
|
|
167149
|
+
'name' in metadata)) {
|
|
167150
|
+
logger.warn(`Invalid CR file ${entry}`);
|
|
167151
|
+
}
|
|
167152
|
+
else {
|
|
167153
|
+
const crKey = `${yamlData.kind}-${metadata.name}`;
|
|
167154
|
+
if (fullResult[crKey]) {
|
|
167155
|
+
logger.error(`Duplicate CR ${crKey}`);
|
|
167156
|
+
}
|
|
167157
|
+
// Keep the complete CR set for validations that need repository-wide context.
|
|
167158
|
+
fullResult[crKey] = yamlData;
|
|
167159
|
+
// Keep the filtered CR set for previous-CR lookup during claim rendering.
|
|
167160
|
+
if (allowedClaimReferences.length === 0 ||
|
|
167161
|
+
allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
|
|
167162
|
+
result[crKey] = yamlData;
|
|
167163
|
+
}
|
|
167164
|
+
}
|
|
167165
|
+
}, excludedPaths.concat(getAdditionalPaths()));
|
|
167166
|
+
return { crs: result, fullCrs: fullResult };
|
|
167167
|
+
}
|
|
167168
|
+
async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
|
|
167169
|
+
const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
|
|
167170
|
+
return crs;
|
|
167171
|
+
}
|
|
167172
|
+
/*
|
|
167173
|
+
* Description: Get the renamed using claimRef annotation
|
|
167174
|
+
* Input: claims: any, crs: any
|
|
167175
|
+
* Output: renames: IRenameResult[]
|
|
167176
|
+
*/
|
|
167177
|
+
async function loadRenames(claims, crs) {
|
|
167178
|
+
/*
|
|
167179
|
+
* If checkRenames is false, we don't need to check for renames
|
|
167180
|
+
*/
|
|
167181
|
+
const renamesEnabled = getRenamesEnabled();
|
|
167182
|
+
if (!renamesEnabled)
|
|
167183
|
+
return [];
|
|
167184
|
+
const result = [];
|
|
167185
|
+
/**
|
|
167186
|
+
* For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
|
|
167187
|
+
*/
|
|
167188
|
+
for (const cr of Object.values(crs)) {
|
|
167189
|
+
const anyCr = cr;
|
|
167190
|
+
if (!anyCr?.metadata?.annotations)
|
|
167191
|
+
continue;
|
|
167192
|
+
// if the annotation is a claimRef
|
|
167193
|
+
const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
|
|
167194
|
+
const annotations = anyCr?.metadata?.annotations || {};
|
|
167195
|
+
if (annotationName in annotations) {
|
|
167196
|
+
const claimRef = anyCr.metadata.annotations[annotationName];
|
|
167197
|
+
// split the annotation into kind and name
|
|
167198
|
+
const [kind, name] = claimRef.split('/');
|
|
167199
|
+
// if the claim exists, check if the name is different for the given provider
|
|
167200
|
+
const referencedClaim = claims[`${kind}-${name}`].claim;
|
|
167201
|
+
const oldName = anyCr.metadata.name;
|
|
167202
|
+
const newName = referencedClaim.providers[getConfiguredProvider()].name;
|
|
167203
|
+
if (oldName !== newName) {
|
|
167204
|
+
result.push({
|
|
167205
|
+
kind: referencedClaim.kind,
|
|
167206
|
+
claimPath: claims[`${kind}-${name}`].claimPath,
|
|
167207
|
+
claimName: referencedClaim.name,
|
|
167208
|
+
oldName,
|
|
167209
|
+
newName,
|
|
167210
|
+
});
|
|
167211
|
+
}
|
|
167212
|
+
}
|
|
167213
|
+
}
|
|
167214
|
+
return result;
|
|
167215
|
+
}
|
|
167216
|
+
async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
|
|
167217
|
+
const data = {
|
|
167218
|
+
renderClaims: {},
|
|
167219
|
+
crs: {},
|
|
167220
|
+
fullCrs: {},
|
|
167221
|
+
};
|
|
167222
|
+
const defaults = loadClaimDefaults();
|
|
167223
|
+
for await (const claimRef of claimRefList) {
|
|
167224
|
+
const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
|
|
167225
|
+
data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
|
|
167226
|
+
}
|
|
167227
|
+
const crClaimReferences = [];
|
|
167228
|
+
for (const ref of Object.keys(data.renderClaims)) {
|
|
167229
|
+
// Replaces only the first instance of '-'
|
|
167230
|
+
crClaimReferences.push(ref.replace('-', '/'));
|
|
167231
|
+
}
|
|
167232
|
+
const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
|
|
167233
|
+
data['crs'] = loadedCrs.crs;
|
|
167234
|
+
data['fullCrs'] = loadedCrs.fullCrs;
|
|
167235
|
+
return data;
|
|
167236
|
+
}
|
|
167237
|
+
function getOrg() {
|
|
167238
|
+
const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
|
|
167239
|
+
return groupOrg;
|
|
167240
|
+
}
|
|
167241
|
+
|
|
167242
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
|
|
167243
|
+
function validateTfStateKeyUniqueness(crs) {
|
|
167244
|
+
const tfStateKeys = {};
|
|
167245
|
+
for (const crKey of Object.keys(crs)) {
|
|
167246
|
+
const cr = crs[crKey];
|
|
167247
|
+
if (!isTerraformWorkspace(cr))
|
|
167248
|
+
continue;
|
|
167249
|
+
const tfKey = cr.spec.firestartr.tfStateKey;
|
|
167250
|
+
if (tfStateKeys[tfKey]) {
|
|
167251
|
+
throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
|
|
167252
|
+
|
|
167253
|
+
The tfStateKey ${tfKey} is not unique,
|
|
167254
|
+
|
|
167255
|
+
It is being used by the following CRs:
|
|
167256
|
+
|
|
167257
|
+
- ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
|
|
167258
|
+
|
|
167259
|
+
- ${cr.kind} ${cr.metadata.name}
|
|
167260
|
+
|
|
167261
|
+
Check the following Claims to ensure that the tfStateKey is unique:
|
|
167262
|
+
|
|
167263
|
+
- ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
|
|
167264
|
+
|
|
167265
|
+
- ${cr.metadata.annotations['firestartr.dev/claim-ref']}
|
|
167266
|
+
`);
|
|
167267
|
+
}
|
|
167268
|
+
tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
|
|
167269
|
+
}
|
|
167270
|
+
}
|
|
167271
|
+
function references_isCatalogEntity(cr) {
|
|
167272
|
+
return [
|
|
167273
|
+
'Component',
|
|
167274
|
+
'Domain',
|
|
167275
|
+
'System',
|
|
167276
|
+
'User',
|
|
167277
|
+
'Group',
|
|
167278
|
+
'Resource',
|
|
167279
|
+
].includes(cr.kind);
|
|
167280
|
+
}
|
|
167281
|
+
function isTerraformWorkspace(cr) {
|
|
167282
|
+
return cr.kind === 'FirestartrTerraformWorkspace';
|
|
167283
|
+
}
|
|
167284
|
+
|
|
167285
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
|
|
167286
|
+
|
|
167287
|
+
const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
|
|
167288
|
+
function validateCrSizes(crs) {
|
|
167289
|
+
for (const key of Object.keys(crs)) {
|
|
167290
|
+
const cr = crs[key];
|
|
167291
|
+
const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
|
|
167292
|
+
const size = Buffer.byteLength(serialized, 'utf8');
|
|
167293
|
+
if (size > K8S_OBJECT_SIZE_LIMIT) {
|
|
167294
|
+
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).`);
|
|
167295
|
+
}
|
|
167296
|
+
}
|
|
167297
|
+
}
|
|
167298
|
+
|
|
167299
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
|
|
167300
|
+
|
|
167301
|
+
function validatePermissionsUniqueness(crs) {
|
|
167302
|
+
for (const crkey in crs) {
|
|
167303
|
+
const cr = crs[crkey];
|
|
167304
|
+
if (cr.kind === 'FirestartrGithubRepository') {
|
|
167305
|
+
const permissions = cr.spec.permissions;
|
|
167306
|
+
logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
|
|
167307
|
+
if (permissions) {
|
|
167308
|
+
const rolesByIdentifier = new Map();
|
|
167309
|
+
for (const perm of permissions) {
|
|
167310
|
+
const identifier = perm.collaborator
|
|
167311
|
+
? perm.collaborator
|
|
167312
|
+
: `ref:${perm.ref.kind}:${perm.ref.name}`;
|
|
167313
|
+
const role = perm.role;
|
|
167314
|
+
const prevRole = rolesByIdentifier.get(identifier);
|
|
167315
|
+
if (prevRole === undefined) {
|
|
167316
|
+
rolesByIdentifier.set(identifier, role);
|
|
167317
|
+
}
|
|
167318
|
+
else if (prevRole !== role) {
|
|
167319
|
+
throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
|
|
167320
|
+
}
|
|
167321
|
+
}
|
|
167322
|
+
}
|
|
167323
|
+
}
|
|
167324
|
+
}
|
|
167325
|
+
}
|
|
167326
|
+
|
|
167327
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
|
|
167328
|
+
const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
|
|
167329
|
+
const CATALOG_KINDS = new Set([
|
|
167330
|
+
'User',
|
|
167331
|
+
'Group',
|
|
167332
|
+
'Component',
|
|
167333
|
+
'System',
|
|
167334
|
+
'Domain',
|
|
167335
|
+
'API',
|
|
167336
|
+
'Resource',
|
|
167337
|
+
'CatalogKubernetesResource',
|
|
167338
|
+
'CatalogTerraformWorkspace',
|
|
167339
|
+
'CatalogArgoDeploy',
|
|
167340
|
+
'CatalogOrgWebhook',
|
|
167341
|
+
'CatalogSecrets',
|
|
167342
|
+
]);
|
|
167343
|
+
function validateNoBackstageAnnotationsInK8sCrs(crs) {
|
|
167344
|
+
for (const crKey in crs) {
|
|
167345
|
+
const cr = crs[crKey];
|
|
167346
|
+
if (CATALOG_KINDS.has(cr.kind))
|
|
167347
|
+
continue;
|
|
167348
|
+
if (cr.metadata?.annotations) {
|
|
167349
|
+
const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
|
|
167350
|
+
if (backstageKeys.length > 0) {
|
|
167351
|
+
throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
|
|
167352
|
+
`Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
|
|
167353
|
+
}
|
|
167354
|
+
}
|
|
167355
|
+
}
|
|
167356
|
+
}
|
|
167357
|
+
|
|
167358
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
|
|
167359
|
+
|
|
167360
|
+
/**
|
|
167361
|
+
* @param renderClaims renderClaims to sort
|
|
167362
|
+
* @param kinds kinds to get from renderClaims
|
|
167363
|
+
* @returns renderClaims with only the kinds specified
|
|
167364
|
+
*/
|
|
167365
|
+
function sortRenderClaimsByKind(renderClaims, kinds) {
|
|
167366
|
+
let result = {};
|
|
167367
|
+
for (const kind of kinds) {
|
|
167368
|
+
for (const key in renderClaims) {
|
|
167369
|
+
if (renderClaims[key].claim.kind === kind) {
|
|
167370
|
+
result[key] = renderClaims[key];
|
|
167371
|
+
}
|
|
167372
|
+
}
|
|
167373
|
+
result = sortSelfKindRefs(result, kind);
|
|
167374
|
+
}
|
|
167375
|
+
return result;
|
|
167376
|
+
}
|
|
167377
|
+
function sortSelfKindRefs(renderClaims, kind) {
|
|
167378
|
+
if (!hasSelfRefs(kind)) {
|
|
167379
|
+
return renderClaims;
|
|
167380
|
+
}
|
|
167381
|
+
const newRenderClaims = {};
|
|
167382
|
+
const data = extractRefs(renderClaims, kind);
|
|
167383
|
+
const ordered = [];
|
|
167384
|
+
const visiting = [];
|
|
167385
|
+
const visited = [];
|
|
167386
|
+
for (const key of Object.keys(data)) {
|
|
167387
|
+
if (!visited.find((obj) => obj.name === key)) {
|
|
167388
|
+
sortRefs(data, data[key], ordered, visiting, visited);
|
|
167389
|
+
}
|
|
167390
|
+
}
|
|
167391
|
+
for (const cr of ordered) {
|
|
167392
|
+
newRenderClaims[cr.name] = renderClaims[cr.name];
|
|
167393
|
+
}
|
|
167394
|
+
return newRenderClaims;
|
|
167395
|
+
}
|
|
167396
|
+
function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
|
|
167397
|
+
if (visiting.find((obj) => obj.name === claim.name)) {
|
|
167398
|
+
throw new Error(`Circular dependency detected for '${claim.name}'`);
|
|
167399
|
+
}
|
|
167400
|
+
if (!visited.find((obj) => obj.name === claim.name)) {
|
|
167401
|
+
visiting.push(claim);
|
|
167402
|
+
if (claim.refs) {
|
|
167403
|
+
for (const ref of claim.refs) {
|
|
167404
|
+
const referencedObject = data[ref];
|
|
167405
|
+
if (!referencedObject) {
|
|
167406
|
+
throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
|
|
167407
|
+
}
|
|
167408
|
+
sortRefs(data, referencedObject, ordered, visiting, visited);
|
|
167409
|
+
}
|
|
167410
|
+
}
|
|
167411
|
+
visited.push(visiting.pop());
|
|
167412
|
+
ordered.push(claim);
|
|
167413
|
+
}
|
|
167414
|
+
}
|
|
167415
|
+
function hasSelfRefs(kind) {
|
|
167416
|
+
return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
|
|
167417
|
+
}
|
|
167418
|
+
|
|
167419
|
+
;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
|
|
167420
|
+
|
|
167421
|
+
function sortKinds(orderedKinds, claims) {
|
|
167422
|
+
const result = [];
|
|
167423
|
+
for (const kind of orderedKinds) {
|
|
167424
|
+
const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
|
|
167425
|
+
result.push(kindRenderClaims);
|
|
167426
|
+
}
|
|
167427
|
+
return result;
|
|
167428
|
+
}
|
|
167429
|
+
|
|
166962
167430
|
;// CONCATENATED MODULE: ../cdk8s_renderer/src/overriders/utils.ts
|
|
166963
167431
|
|
|
166964
167432
|
|
|
@@ -166974,20 +167442,11 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
|
|
|
166974
167442
|
files.push({ path, userManaged, content, targetBranch });
|
|
166975
167443
|
}
|
|
166976
167444
|
const featName = catalog_common/* default.generic.normalizeName */.Z.generic.normalizeName(`${featureName}-${cr.spec.firestartr.tfStateKey}`);
|
|
166977
|
-
const traceability = featureOutputObject.traceability || {};
|
|
166978
167445
|
return {
|
|
166979
167446
|
kind: 'GithubRepositoryFeatureClaim',
|
|
166980
167447
|
version: '1.0.0',
|
|
166981
167448
|
name: featName,
|
|
166982
|
-
feature: {
|
|
166983
|
-
name: featureName,
|
|
166984
|
-
version: featureVersion,
|
|
166985
|
-
ref: traceability.ref,
|
|
166986
|
-
repo: traceability.repo,
|
|
166987
|
-
sha: traceability.sha,
|
|
166988
|
-
tags: traceability.tags,
|
|
166989
|
-
url: traceability.url,
|
|
166990
|
-
},
|
|
167449
|
+
feature: { name: featureName, version: featureVersion },
|
|
166991
167450
|
firestartr: { tfStateKey: cr.spec.firestartr.tfStateKey },
|
|
166992
167451
|
files,
|
|
166993
167452
|
claimRepoName,
|
|
@@ -167001,6 +167460,16 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
|
|
|
167001
167460
|
},
|
|
167002
167461
|
branch: cr.spec.repo.branch,
|
|
167003
167462
|
},
|
|
167463
|
+
// Attach provider-keyed claimPatches if present in the feature render
|
|
167464
|
+
// output. This is a separate attach path used when the claim is created
|
|
167465
|
+
// elsewhere (the claim object here will be merged by the caller into the
|
|
167466
|
+
// created claim); it is distinct from `patches`, which claim stitching
|
|
167467
|
+
// consumes directly. Keep this field optional.
|
|
167468
|
+
...(featureOutputObject.claimPatches
|
|
167469
|
+
? {
|
|
167470
|
+
claimPatches: featureOutputObject.claimPatches,
|
|
167471
|
+
}
|
|
167472
|
+
: {}),
|
|
167004
167473
|
};
|
|
167005
167474
|
}
|
|
167006
167475
|
|
|
@@ -167009,14 +167478,12 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
|
|
|
167009
167478
|
|
|
167010
167479
|
|
|
167011
167480
|
|
|
167012
|
-
|
|
167013
|
-
|
|
167014
167481
|
// this is the only way currently to make
|
|
167015
167482
|
// work the system and inject our code
|
|
167016
167483
|
// without having to download a feature
|
|
167017
|
-
let
|
|
167484
|
+
let featureOverride_MOCK_FEATURES_FN;
|
|
167018
167485
|
function MOCK_FEATURES(mock) {
|
|
167019
|
-
|
|
167486
|
+
featureOverride_MOCK_FEATURES_FN = mock;
|
|
167020
167487
|
if (mock) {
|
|
167021
167488
|
log.warn('⚠️ Warning features system downloader has been mocked, it should only happen in unit testing');
|
|
167022
167489
|
}
|
|
@@ -167047,8 +167514,8 @@ class FeaturesOverrider extends OverriderPatches {
|
|
|
167047
167514
|
let owner = 'prefapp';
|
|
167048
167515
|
let versionOrRef = this.featureVersion;
|
|
167049
167516
|
let renderer = 'getFeatureConfig';
|
|
167050
|
-
if (
|
|
167051
|
-
return
|
|
167517
|
+
if (featureOverride_MOCK_FEATURES_FN) {
|
|
167518
|
+
return featureOverride_MOCK_FEATURES_FN();
|
|
167052
167519
|
}
|
|
167053
167520
|
if (this.featureRepo) {
|
|
167054
167521
|
[owner, repo] = this.featureRepo.split(/\//);
|
|
@@ -167066,8 +167533,8 @@ class FeaturesOverrider extends OverriderPatches {
|
|
|
167066
167533
|
const featureName = this.featureName;
|
|
167067
167534
|
const featureVersion = this.featureVersion || this.featureRef;
|
|
167068
167535
|
const featureArgs = this.featureArgs;
|
|
167069
|
-
const fSetUpAndRunRenderer = function (
|
|
167070
|
-
return this.setUpAndRunRenderer(
|
|
167536
|
+
const fSetUpAndRunRenderer = function (target) {
|
|
167537
|
+
return this.setUpAndRunRenderer(target);
|
|
167071
167538
|
}.bind(this);
|
|
167072
167539
|
let renderedFeatureConfig = null;
|
|
167073
167540
|
return [
|
|
@@ -167076,7 +167543,7 @@ class FeaturesOverrider extends OverriderPatches {
|
|
|
167076
167543
|
return true;
|
|
167077
167544
|
},
|
|
167078
167545
|
async apply(cr) {
|
|
167079
|
-
renderedFeatureConfig = await fSetUpAndRunRenderer(
|
|
167546
|
+
renderedFeatureConfig = await fSetUpAndRunRenderer(claim);
|
|
167080
167547
|
return cr;
|
|
167081
167548
|
},
|
|
167082
167549
|
identify() {
|
|
@@ -167089,28 +167556,6 @@ class FeaturesOverrider extends OverriderPatches {
|
|
|
167089
167556
|
return utils_renderFeature(featureName, featureVersion, renderedFeatureConfig, cr, claim.name);
|
|
167090
167557
|
},
|
|
167091
167558
|
},
|
|
167092
|
-
{
|
|
167093
|
-
isPostPatch: true,
|
|
167094
|
-
validate() {
|
|
167095
|
-
return true;
|
|
167096
|
-
},
|
|
167097
|
-
async apply(cr) {
|
|
167098
|
-
if (!renderedFeatureConfig) {
|
|
167099
|
-
logger.warn(`Feature ${featureName}: renderedFeatureConfig is null, skipping post-patch`);
|
|
167100
|
-
return cr;
|
|
167101
|
-
}
|
|
167102
|
-
const provider = helperCTX(this).provider;
|
|
167103
|
-
const jsonPatches = renderedFeatureConfig.patches?.[provider] || [];
|
|
167104
|
-
fast_json_patch_default().applyPatch(cr, jsonPatches);
|
|
167105
|
-
return cr;
|
|
167106
|
-
},
|
|
167107
|
-
identify() {
|
|
167108
|
-
return `feature-json-patch/${featureName}`;
|
|
167109
|
-
},
|
|
167110
|
-
applicable() {
|
|
167111
|
-
return { applicableProviders: ['catalog', 'github'] };
|
|
167112
|
-
},
|
|
167113
|
-
},
|
|
167114
167559
|
];
|
|
167115
167560
|
}
|
|
167116
167561
|
}
|
|
@@ -170854,29 +171299,13 @@ class FeatureRepoChart extends BaseGithubChart {
|
|
|
170854
171299
|
template() {
|
|
170855
171300
|
const claim = this.get('claim');
|
|
170856
171301
|
const repositoryTarget = this.resolveRepositoryTarget();
|
|
170857
|
-
const annotations = {
|
|
170858
|
-
'firestartr.dev/external-name': claim.name,
|
|
170859
|
-
'firestartr.dev/feature-name': claim.feature.name,
|
|
170860
|
-
};
|
|
170861
|
-
if (claim.feature.sha) {
|
|
170862
|
-
annotations['firestartr.dev/feature-git-sha'] = claim.feature.sha;
|
|
170863
|
-
}
|
|
170864
|
-
if (claim.feature.tags?.length) {
|
|
170865
|
-
annotations['firestartr.dev/feature-git-tags'] = JSON.stringify(claim.feature.tags);
|
|
170866
|
-
}
|
|
170867
|
-
if (claim.feature.url) {
|
|
170868
|
-
annotations['firestartr.dev/feature-url'] = claim.feature.url;
|
|
170869
|
-
}
|
|
170870
|
-
if (claim.feature.ref) {
|
|
170871
|
-
annotations['firestartr.dev/feature-ref'] = claim.feature.ref;
|
|
170872
|
-
}
|
|
170873
|
-
if (claim.feature.repo) {
|
|
170874
|
-
annotations['firestartr.dev/feature-repo'] = claim.feature.repo;
|
|
170875
|
-
}
|
|
170876
171302
|
const cr = {
|
|
170877
171303
|
metadata: {
|
|
170878
171304
|
name: claim.name,
|
|
170879
|
-
annotations
|
|
171305
|
+
annotations: {
|
|
171306
|
+
'firestartr.dev/external-name': claim.name,
|
|
171307
|
+
'firestartr.dev/feature-name': claim.feature.name,
|
|
171308
|
+
},
|
|
170880
171309
|
},
|
|
170881
171310
|
spec: {
|
|
170882
171311
|
context: claim.context,
|
|
@@ -170896,7 +171325,7 @@ class FeatureRepoChart extends BaseGithubChart {
|
|
|
170896
171325
|
},
|
|
170897
171326
|
},
|
|
170898
171327
|
};
|
|
170899
|
-
const
|
|
171328
|
+
const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
|
|
170900
171329
|
'claim-ref',
|
|
170901
171330
|
'revision',
|
|
170902
171331
|
'sync-enabled',
|
|
@@ -170904,7 +171333,7 @@ class FeatureRepoChart extends BaseGithubChart {
|
|
|
170904
171333
|
]);
|
|
170905
171334
|
cr.metadata.annotations = {
|
|
170906
171335
|
...cr.metadata.annotations,
|
|
170907
|
-
...
|
|
171336
|
+
...annotations,
|
|
170908
171337
|
};
|
|
170909
171338
|
// We do this to remove undefined variables from the template
|
|
170910
171339
|
const crTemplate = JSON.parse(JSON.stringify(cr, null, 2));
|
|
@@ -363578,7 +364007,7 @@ function helperCreateCheckRunName(cmd, item) {
|
|
|
363578
364007
|
|
|
363579
364008
|
;// CONCATENATED MODULE: ../operator/src/utils/index.ts
|
|
363580
364009
|
const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
|
|
363581
|
-
function
|
|
364010
|
+
function utils_isRecord(value) {
|
|
363582
364011
|
return typeof value === 'object' && value !== null;
|
|
363583
364012
|
}
|
|
363584
364013
|
function getFirstNonEmptyString(...values) {
|
|
@@ -363616,7 +364045,7 @@ function replaceInlineSecrets(inline, secrets) {
|
|
|
363616
364045
|
return result;
|
|
363617
364046
|
}
|
|
363618
364047
|
function extractErrorDetails(error) {
|
|
363619
|
-
const exitCode =
|
|
364048
|
+
const exitCode = utils_isRecord(error) && typeof error.exitCode === 'number'
|
|
363620
364049
|
? error.exitCode
|
|
363621
364050
|
: undefined;
|
|
363622
364051
|
if (typeof error === 'string') {
|
|
@@ -363627,7 +364056,7 @@ function extractErrorDetails(error) {
|
|
|
363627
364056
|
const output = getFirstNonEmptyString(errorWithOutput.output, error.message);
|
|
363628
364057
|
return { output: output ?? String(error), exitCode };
|
|
363629
364058
|
}
|
|
363630
|
-
if (
|
|
364059
|
+
if (utils_isRecord(error)) {
|
|
363631
364060
|
const output = getFirstNonEmptyString(error.output, error.message, error.error);
|
|
363632
364061
|
if (output !== undefined) {
|
|
363633
364062
|
return { output, exitCode };
|
|
@@ -713963,7 +714392,7 @@ const crsStatusSubcommand = {
|
|
|
713963
714392
|
};
|
|
713964
714393
|
|
|
713965
714394
|
;// CONCATENATED MODULE: ./package.json
|
|
713966
|
-
const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-
|
|
714395
|
+
const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-patches-patch-claims-2"}');
|
|
713967
714396
|
;// CONCATENATED MODULE: ../../package.json
|
|
713968
714397
|
const package_namespaceObject_1 = {"i8":"2.10.0"};
|
|
713969
714398
|
// EXTERNAL MODULE: ../../node_modules/semver/index.js
|