@firestartr/cli 2.1.0-snapshot → 2.1.0
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 +63 -61
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -356187,39 +356187,33 @@ function createCRrefFrom(claimRef, needsSecret) {
|
|
|
356187
356187
|
* @returns string
|
|
356188
356188
|
*/
|
|
356189
356189
|
function createCodeOwnersData(claim, additionalRules) {
|
|
356190
|
-
const
|
|
356191
|
-
|
|
356192
|
-
|
|
356193
|
-
|
|
356194
|
-
|
|
356195
|
-
return messageFinal;
|
|
356190
|
+
const rulesMap = new Map();
|
|
356191
|
+
const addOwnerToPath = (path, owner) => {
|
|
356192
|
+
const owners = rulesMap.get(path) ?? [];
|
|
356193
|
+
if (!owners.includes(owner)) {
|
|
356194
|
+
owners.push(owner);
|
|
356196
356195
|
}
|
|
356197
|
-
|
|
356198
|
-
redactedLines.push(line);
|
|
356199
|
-
messageFinal += line;
|
|
356200
|
-
}
|
|
356201
|
-
return messageFinal;
|
|
356196
|
+
rulesMap.set(path, owners);
|
|
356202
356197
|
};
|
|
356203
356198
|
let message = `# This file was generated by firestartr.
|
|
356204
356199
|
# WARNING: Please don't edit this file directly in the repository
|
|
356205
356200
|
# Go to gitops repository to modify it!`;
|
|
356206
356201
|
if (claim.owner) {
|
|
356207
|
-
|
|
356202
|
+
addOwnerToPath('*', resolveCodeownersRef(claim.owner, claim.providers.github.org));
|
|
356208
356203
|
}
|
|
356209
356204
|
if (claim.platformOwner) {
|
|
356210
|
-
|
|
356205
|
+
addOwnerToPath('/.github/', resolveCodeownersRef(claim.platformOwner, claim.providers.github.org));
|
|
356211
356206
|
}
|
|
356212
356207
|
if (additionalRules) {
|
|
356213
356208
|
for (const rule of additionalRules) {
|
|
356214
|
-
let line = `\n${rule.path.padEnd(25)}`;
|
|
356215
356209
|
for (const owner of rule.owners) {
|
|
356216
|
-
|
|
356210
|
+
addOwnerToPath(rule.path, resolveCodeownersRef(owner, claim.providers.github.org));
|
|
356217
356211
|
}
|
|
356218
|
-
message = concatLine(message, line);
|
|
356219
|
-
// Remove trailing <space> from the previous loop
|
|
356220
|
-
message = message.substring(0, message.length - 1);
|
|
356221
356212
|
}
|
|
356222
356213
|
}
|
|
356214
|
+
for (const [path, owners] of rulesMap.entries()) {
|
|
356215
|
+
message += `\n${path.padEnd(25)} ${owners.join(' ')}`;
|
|
356216
|
+
}
|
|
356223
356217
|
return message;
|
|
356224
356218
|
}
|
|
356225
356219
|
function resolveCodeownersRef(ref, org) {
|
|
@@ -367438,12 +367432,12 @@ class RepoGithubDecanter extends GithubDecanter {
|
|
|
367438
367432
|
});
|
|
367439
367433
|
}
|
|
367440
367434
|
const directWriters = this.data.teamsAndMembers.directMembers
|
|
367441
|
-
.filter((member) => member.role === '
|
|
367435
|
+
.filter((member) => member.role === 'write')
|
|
367442
367436
|
.map((member) => {
|
|
367443
367437
|
return `user-imported-ref:${member.name}`;
|
|
367444
367438
|
});
|
|
367445
367439
|
const outsideWriters = this.data.teamsAndMembers.outsideMembers
|
|
367446
|
-
.filter((member) => member.role === '
|
|
367440
|
+
.filter((member) => member.role === 'write')
|
|
367447
367441
|
.map((member) => {
|
|
367448
367442
|
return `collaborator:${member.name.toLowerCase()}`;
|
|
367449
367443
|
});
|
|
@@ -367462,12 +367456,12 @@ class RepoGithubDecanter extends GithubDecanter {
|
|
|
367462
367456
|
});
|
|
367463
367457
|
}
|
|
367464
367458
|
const directReaders = this.data.teamsAndMembers.directMembers
|
|
367465
|
-
.filter((member) => member.role === '
|
|
367459
|
+
.filter((member) => member.role === 'read')
|
|
367466
367460
|
.map((member) => {
|
|
367467
367461
|
return `user-imported-ref:${member.name}`;
|
|
367468
367462
|
});
|
|
367469
367463
|
const outsideReaders = this.data.teamsAndMembers.outsideMembers
|
|
367470
|
-
.filter((member) => member.role === '
|
|
367464
|
+
.filter((member) => member.role === 'read')
|
|
367471
367465
|
.map((member) => {
|
|
367472
367466
|
return `collaborator:${member.name.toLowerCase()}`;
|
|
367473
367467
|
});
|
|
@@ -367502,7 +367496,11 @@ class RepoGithubDecanter extends GithubDecanter {
|
|
|
367502
367496
|
};
|
|
367503
367497
|
});
|
|
367504
367498
|
this.data['teamsAndMembers']['teams'] = teams;
|
|
367505
|
-
|
|
367499
|
+
// GitHub's `affiliation=direct` includes outside collaborators, so we must
|
|
367500
|
+
// exclude them from directMembers to avoid tagging them as user-imported-ref.
|
|
367501
|
+
const outsideMemberNames = new Set(outsideMembers.map((m) => m.name.toLowerCase()));
|
|
367502
|
+
const filteredDirectMembers = directMembers.filter((m) => !outsideMemberNames.has(m.name.toLowerCase()));
|
|
367503
|
+
this.data['teamsAndMembers']['directMembers'] = filteredDirectMembers;
|
|
367506
367504
|
this.data['teamsAndMembers']['outsideMembers'] = outsideMembers;
|
|
367507
367505
|
}
|
|
367508
367506
|
async __gatherPages() {
|
|
@@ -367923,7 +367921,7 @@ async function reimportGithubGitopsRepository(org, crsPath, configPath, generate
|
|
|
367923
367921
|
}
|
|
367924
367922
|
cr.metadata.annotations[reImportAnnotation] = 'true';
|
|
367925
367923
|
cr.metadata.annotations[importAnnotation] = 'true';
|
|
367926
|
-
// we set an
|
|
367924
|
+
// we set an annotation to trigger the update
|
|
367927
367925
|
cr.metadata.annotations[reconcileAtAnnotation] = new Date()
|
|
367928
367926
|
.toISOString()
|
|
367929
367927
|
.replace(/\.\d{3}Z$/, 'Z'); // exact format: 2026-04-19T20:45:00Z
|
|
@@ -367935,10 +367933,10 @@ async function reimportGithubGitopsRepository(org, crsPath, configPath, generate
|
|
|
367935
367933
|
// we need to search for dependencies if proceeds
|
|
367936
367934
|
if (!collection.IS_SKIP_SET(generatedFilters, 'gh-repo')) {
|
|
367937
367935
|
importer_src_logger.info(`Searching for dependencies to annotate with ${reImportAnnotation} and ${importAnnotation}`);
|
|
367938
|
-
await searchForDependencies(org, crsPath, configPath, generatedFilters, crsWithDependenciesToAnnotate, reImportAnnotation, importAnnotation);
|
|
367936
|
+
await searchForDependencies(org, crsPath, configPath, generatedFilters, crsWithDependenciesToAnnotate, reImportAnnotation, importAnnotation, reconcileAtAnnotation);
|
|
367939
367937
|
}
|
|
367940
367938
|
}
|
|
367941
|
-
async function searchForDependencies(org, crsPath, configPath, generatedFilters, crsWithDependenciesToAnnotate, reImportAnnotation, importAnnotation) {
|
|
367939
|
+
async function searchForDependencies(org, crsPath, configPath, generatedFilters, crsWithDependenciesToAnnotate, reImportAnnotation, importAnnotation, reconcileAtAnnotation) {
|
|
367942
367940
|
// we need to perform a search in all the crsPath
|
|
367943
367941
|
await searchCRs(crsPath, async (cr, filePath) => {
|
|
367944
367942
|
importer_src_logger.debug(`${JSON.stringify(cr)}`);
|
|
@@ -367956,6 +367954,10 @@ async function searchForDependencies(org, crsPath, configPath, generatedFilters,
|
|
|
367956
367954
|
}
|
|
367957
367955
|
cr.metadata.annotations[reImportAnnotation] = 'true';
|
|
367958
367956
|
cr.metadata.annotations[importAnnotation] = 'true';
|
|
367957
|
+
// we set an annotation to trigger the update
|
|
367958
|
+
cr.metadata.annotations[reconcileAtAnnotation] = new Date()
|
|
367959
|
+
.toISOString()
|
|
367960
|
+
.replace(/\.\d{3}Z$/, 'Z'); // exact format: 2026-04-19T20:45:00Z
|
|
367959
367961
|
// we write the cr back to the file system
|
|
367960
367962
|
const newContent = catalog_common.io.toYaml(cr);
|
|
367961
367963
|
await promises_default().writeFile(filePath, newContent, 'utf-8');
|
|
@@ -369061,7 +369063,7 @@ function shouldForceReconcileByAnnotation(cr) {
|
|
|
369061
369063
|
operator_src_logger.warn(`Timestamp is invalid for the annotation ${cr.kind}/${cr.metadata?.name || 'unknown'}: ${annotationValue}`);
|
|
369062
369064
|
return false;
|
|
369063
369065
|
}
|
|
369064
|
-
//
|
|
369066
|
+
// Find the most recent timestamp across all conditions
|
|
369065
369067
|
let latestConditionTime = 0;
|
|
369066
369068
|
for (const condition of cr.status?.conditions || []) {
|
|
369067
369069
|
const timeStr = condition.lastUpdateTime || condition.lastTransitionTime;
|
|
@@ -369073,7 +369075,7 @@ function shouldForceReconcileByAnnotation(cr) {
|
|
|
369073
369075
|
}
|
|
369074
369076
|
}
|
|
369075
369077
|
const shouldForce = annotationTime > latestConditionTime;
|
|
369076
|
-
operator_src_logger.debug(
|
|
369078
|
+
operator_src_logger.debug(`Evaluating force-reconcile for ${cr.metadata.name}: annotation time (${new Date(annotationTime).toISOString()}) vs latest condition time (${new Date(latestConditionTime).toISOString()})`, { metadata: { shouldForce } });
|
|
369077
369079
|
if (shouldForce) {
|
|
369078
369080
|
operator_src_logger.info(`force-reconcile activated for ${cr.metadata.name} → annotation (${annotationValue}) is newer than latest condition time (${new Date(latestConditionTime).toISOString()})`);
|
|
369079
369081
|
}
|
|
@@ -370757,6 +370759,19 @@ function getQueueMetrics() {
|
|
|
370757
370759
|
},
|
|
370758
370760
|
};
|
|
370759
370761
|
}
|
|
370762
|
+
// we need to assign weight to the deletion operation
|
|
370763
|
+
// to avoid blockades
|
|
370764
|
+
// https://github.com/prefapp/gitops-k8s/issues/1864
|
|
370765
|
+
// ghrepo feat | grss -> ghrepo -> ghgroup -> membership
|
|
370766
|
+
const DELETION_WEIGHTS = {
|
|
370767
|
+
FirestartrGithubRepositoryFeature: 5,
|
|
370768
|
+
FirestartrGithubRepositorySecretsSection: 4,
|
|
370769
|
+
FirestartrGithubRepository: 3,
|
|
370770
|
+
FirestartrGithubGroup: 2,
|
|
370771
|
+
FirestartrGithubMembership: 1,
|
|
370772
|
+
FirestartrTerraformWorkspace: 1,
|
|
370773
|
+
FirestartrGithubOrgWebhook: 1,
|
|
370774
|
+
};
|
|
370760
370775
|
// Do the kinds need different weights for the operations?
|
|
370761
370776
|
// We need to discuss this with the team in this issue: https://github.com/prefapp/gitops-k8s/issues/524
|
|
370762
370777
|
// const KIND_WEIGHTS: any = {
|
|
@@ -370826,7 +370841,17 @@ function sortQueue(queue) {
|
|
|
370826
370841
|
// If weightA is larger, return -1 (a comes before b)
|
|
370827
370842
|
return weightB - weightA;
|
|
370828
370843
|
}
|
|
370829
|
-
//
|
|
370844
|
+
// in case of deletions we need to
|
|
370845
|
+
// establish a secondary sort by kind
|
|
370846
|
+
if (wa.operation === 'MARKED_TO_DELETION' &&
|
|
370847
|
+
wb.operation === 'MARKED_TO_DELETION') {
|
|
370848
|
+
const deletionWeightA = DELETION_WEIGHTS[wa.item.kind] || 0;
|
|
370849
|
+
const deletionWeightB = DELETION_WEIGHTS[wb.item.kind] || 0;
|
|
370850
|
+
if (deletionWeightA !== deletionWeightB) {
|
|
370851
|
+
return deletionWeightB - deletionWeightA;
|
|
370852
|
+
}
|
|
370853
|
+
}
|
|
370854
|
+
// --- 3rd SORT: By upsertTime (Ascending) ---
|
|
370830
370855
|
// If weights are equal, sort by the oldest upsertTime (ascending)
|
|
370831
370856
|
return wa.upsertTime - wb.upsertTime;
|
|
370832
370857
|
});
|
|
@@ -371084,6 +371109,7 @@ function processHandler(processToHandle, ctl, onTimedOut) {
|
|
|
371084
371109
|
|
|
371085
371110
|
|
|
371086
371111
|
|
|
371112
|
+
const TOFU_LOCK_TIMEOUT = '-lock-timeout=60s';
|
|
371087
371113
|
async function utils_validate(path, secrets) {
|
|
371088
371114
|
return await tfExec(path, ['validate'], secrets);
|
|
371089
371115
|
}
|
|
@@ -371104,7 +371130,7 @@ async function plan(path, secrets, format, args = ['plan'], stream, ctl) {
|
|
|
371104
371130
|
}
|
|
371105
371131
|
async function apply(path, secrets, stream, ctl) {
|
|
371106
371132
|
terraform_provisioner_src_logger.debug(`Running terraform apply in path ${path}`);
|
|
371107
|
-
return await tfExec(path, ['apply', '-auto-approve'], secrets, ['-input=false'], stream, ctl);
|
|
371133
|
+
return await tfExec(path, ['apply', '-auto-approve', TOFU_LOCK_TIMEOUT], secrets, ['-input=false'], stream, ctl);
|
|
371108
371134
|
}
|
|
371109
371135
|
async function customCommand(path, secrets, args, stream) {
|
|
371110
371136
|
terraform_provisioner_src_logger.debug(`Running terraform customCommand in path ${path} ${args.join(',')}`);
|
|
@@ -371112,7 +371138,7 @@ async function customCommand(path, secrets, args, stream) {
|
|
|
371112
371138
|
}
|
|
371113
371139
|
async function destroy(path, secrets, stream, ctl) {
|
|
371114
371140
|
terraform_provisioner_src_logger.debug(`Running terraform destroy in path ${path}`);
|
|
371115
|
-
return await tfExec(path, ['destroy', '-auto-approve'], secrets, ['-input=false'], stream, ctl);
|
|
371141
|
+
return await tfExec(path, ['destroy', '-auto-approve', TOFU_LOCK_TIMEOUT], secrets, ['-input=false'], stream, ctl);
|
|
371116
371142
|
}
|
|
371117
371143
|
async function output(path, secrets) {
|
|
371118
371144
|
terraform_provisioner_src_logger.debug(`Running terraform output in path ${path}`);
|
|
@@ -375266,47 +375292,23 @@ async function endDebug(entity) {
|
|
|
375266
375292
|
|
|
375267
375293
|
|
|
375268
375294
|
const tp_bridge_TF_PROJECTS_PATH = '/tmp/gh-workspaces';
|
|
375269
|
-
const STATE_LOCK_RETRY_ATTEMPTS = 3;
|
|
375270
|
-
const STATE_LOCK_RETRY_DELAY_MS = 5000;
|
|
375271
|
-
function isStateLockError(error) {
|
|
375272
|
-
const message = String(error);
|
|
375273
|
-
return (message.includes('Error acquiring the state lock') ||
|
|
375274
|
-
message.includes('already locked by another tofu client'));
|
|
375275
|
-
}
|
|
375276
|
-
async function tp_bridge_sleep(ms) {
|
|
375277
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
375278
|
-
}
|
|
375279
|
-
async function runTerraformProvisionerWithLockRetry(entity, command, streaming, customArgs, ctl) {
|
|
375280
|
-
for (let attempt = 1;; attempt++) {
|
|
375281
|
-
try {
|
|
375282
|
-
return await runTerraformProvisioner(tp_bridge_buildContext(entity), command, streaming, customArgs, ctl);
|
|
375283
|
-
}
|
|
375284
|
-
catch (error) {
|
|
375285
|
-
if (!isStateLockError(error) || attempt >= STATE_LOCK_RETRY_ATTEMPTS) {
|
|
375286
|
-
throw error;
|
|
375287
|
-
}
|
|
375288
|
-
gh_provisioner_src_logger.warn(`State lock detected for ${entity.k8sId} while running '${command}'. Retrying in ${STATE_LOCK_RETRY_DELAY_MS}ms (attempt ${attempt + 1}/${STATE_LOCK_RETRY_ATTEMPTS}).`);
|
|
375289
|
-
await tp_bridge_sleep(STATE_LOCK_RETRY_DELAY_MS);
|
|
375290
|
-
}
|
|
375291
|
-
}
|
|
375292
|
-
}
|
|
375293
375295
|
async function runOnTerraform(entity, command, customArgs, opts = {}) {
|
|
375294
375296
|
gh_provisioner_src_logger.info(`Running on terraform entity '${entity.k8sId}' with command '${command}' customArgs = ${customArgs ? customArgs.join(',') : 'null'} `);
|
|
375295
375297
|
const streaming = entity.streamTFProvisioner;
|
|
375296
375298
|
if (command === 'import-with-reimport') {
|
|
375297
375299
|
// we nuke the tf state
|
|
375298
|
-
await
|
|
375300
|
+
await runTerraformProvisioner(tp_bridge_buildContext(entity), 'destroy-state-only', streaming, customArgs, opts.ctl);
|
|
375299
375301
|
// we get the elements we need to import
|
|
375300
375302
|
await entity.loadAddressesToImport();
|
|
375301
|
-
return await
|
|
375303
|
+
return await runTerraformProvisioner(tp_bridge_buildContext(entity), 'custom-import', streaming, entity.importDocument, opts.ctl);
|
|
375302
375304
|
}
|
|
375303
375305
|
else if (command === 'import') {
|
|
375304
375306
|
// we get the elements we need to import
|
|
375305
375307
|
await entity.loadAddressesToImport();
|
|
375306
|
-
return await
|
|
375308
|
+
return await runTerraformProvisioner(tp_bridge_buildContext(entity), 'custom-import', streaming, entity.importDocument, opts.ctl);
|
|
375307
375309
|
}
|
|
375308
375310
|
else {
|
|
375309
|
-
return await
|
|
375311
|
+
return await runTerraformProvisioner(tp_bridge_buildContext(entity), command, streaming, customArgs, opts.ctl);
|
|
375310
375312
|
}
|
|
375311
375313
|
}
|
|
375312
375314
|
function tp_bridge_buildContext(entity) {
|
|
@@ -379455,9 +379457,9 @@ const crs_analyzerSubcommand = {
|
|
|
379455
379457
|
};
|
|
379456
379458
|
|
|
379457
379459
|
;// CONCATENATED MODULE: ./package.json
|
|
379458
|
-
const package_namespaceObject =
|
|
379460
|
+
const package_namespaceObject = {"i8":"2.1.0"};
|
|
379459
379461
|
;// CONCATENATED MODULE: ../../package.json
|
|
379460
|
-
const package_namespaceObject_1 = {"i8":"2.
|
|
379462
|
+
const package_namespaceObject_1 = {"i8":"2.1.0"};
|
|
379461
379463
|
;// CONCATENATED MODULE: ./src/subcommands/index.ts
|
|
379462
379464
|
|
|
379463
379465
|
|