@postman-cse/onboarding-repo-sync 2.1.4 → 2.1.6
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/README.md +9 -0
- package/action.yml +27 -1
- package/dist/action.cjs +882 -104
- package/dist/cli.cjs +1338 -108
- package/dist/index.cjs +892 -104
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -92334,11 +92334,11 @@ var require_valid = __commonJS({
|
|
|
92334
92334
|
var require_clean = __commonJS({
|
|
92335
92335
|
"node_modules/postman-collection/node_modules/semver/functions/clean.js"(exports2, module2) {
|
|
92336
92336
|
var parse2 = require_parse5();
|
|
92337
|
-
var
|
|
92337
|
+
var clean2 = (version2, options) => {
|
|
92338
92338
|
const s = parse2(version2.trim().replace(/^[=v]+/, ""), options);
|
|
92339
92339
|
return s ? s.version : null;
|
|
92340
92340
|
};
|
|
92341
|
-
module2.exports =
|
|
92341
|
+
module2.exports = clean2;
|
|
92342
92342
|
}
|
|
92343
92343
|
});
|
|
92344
92344
|
|
|
@@ -93649,7 +93649,7 @@ var require_semver2 = __commonJS({
|
|
|
93649
93649
|
var identifiers = require_identifiers();
|
|
93650
93650
|
var parse2 = require_parse5();
|
|
93651
93651
|
var valid = require_valid();
|
|
93652
|
-
var
|
|
93652
|
+
var clean2 = require_clean();
|
|
93653
93653
|
var inc = require_inc();
|
|
93654
93654
|
var diff = require_diff();
|
|
93655
93655
|
var major = require_major();
|
|
@@ -93687,7 +93687,7 @@ var require_semver2 = __commonJS({
|
|
|
93687
93687
|
module2.exports = {
|
|
93688
93688
|
parse: parse2,
|
|
93689
93689
|
valid,
|
|
93690
|
-
clean,
|
|
93690
|
+
clean: clean2,
|
|
93691
93691
|
inc,
|
|
93692
93692
|
diff,
|
|
93693
93693
|
major,
|
|
@@ -119798,7 +119798,7 @@ __export(cli_exports, {
|
|
|
119798
119798
|
});
|
|
119799
119799
|
module.exports = __toCommonJS(cli_exports);
|
|
119800
119800
|
var import_node_child_process = require("node:child_process");
|
|
119801
|
-
var
|
|
119801
|
+
var import_node_fs5 = require("node:fs");
|
|
119802
119802
|
var import_promises = require("node:fs/promises");
|
|
119803
119803
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
119804
119804
|
var import_node_util = require("node:util");
|
|
@@ -120166,7 +120166,7 @@ var ExitCode;
|
|
|
120166
120166
|
})(ExitCode || (ExitCode = {}));
|
|
120167
120167
|
|
|
120168
120168
|
// src/index.ts
|
|
120169
|
-
var
|
|
120169
|
+
var import_node_fs4 = require("node:fs");
|
|
120170
120170
|
var path3 = __toESM(require("node:path"), 1);
|
|
120171
120171
|
|
|
120172
120172
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -123289,6 +123289,9 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
123289
123289
|
" branches: [main]",
|
|
123290
123290
|
" schedule:",
|
|
123291
123291
|
' - cron: "0 */6 * * *"',
|
|
123292
|
+
"concurrency:",
|
|
123293
|
+
" group: postman-onboard-${{ github.head_ref || github.ref_name }}",
|
|
123294
|
+
" cancel-in-progress: false",
|
|
123292
123295
|
"jobs:",
|
|
123293
123296
|
" test:",
|
|
123294
123297
|
" runs-on: ubuntu-latest",
|
|
@@ -123305,7 +123308,7 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
123305
123308
|
" ruby <<'RUBY'",
|
|
123306
123309
|
" require 'yaml'",
|
|
123307
123310
|
" config = YAML.load_file('.postman/resources.yaml') || {}",
|
|
123308
|
-
" cloud = config.fetch('cloudResources', {})",
|
|
123311
|
+
" cloud = config.fetch('canonical', config.fetch('cloudResources', {}))",
|
|
123309
123312
|
" collections = cloud.fetch('collections', {})",
|
|
123310
123313
|
" environments = cloud.fetch('environments', {})",
|
|
123311
123314
|
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
@@ -123503,6 +123506,58 @@ function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
123503
123506
|
""
|
|
123504
123507
|
];
|
|
123505
123508
|
}
|
|
123509
|
+
function renderGcWorkflowTemplate() {
|
|
123510
|
+
return [
|
|
123511
|
+
"name: Postman Preview GC",
|
|
123512
|
+
"on:",
|
|
123513
|
+
" delete:",
|
|
123514
|
+
' branches: ["**"]',
|
|
123515
|
+
" pull_request:",
|
|
123516
|
+
" types: [closed]",
|
|
123517
|
+
" schedule:",
|
|
123518
|
+
' - cron: "0 2 * * *"',
|
|
123519
|
+
" workflow_dispatch:",
|
|
123520
|
+
" inputs:",
|
|
123521
|
+
" branch:",
|
|
123522
|
+
" description: Branch name to GC (optional, otherwise sweep by TTL/branch-existence)",
|
|
123523
|
+
" required: false",
|
|
123524
|
+
"concurrency:",
|
|
123525
|
+
" group: postman-preview-gc-${{ github.ref_name }}",
|
|
123526
|
+
" cancel-in-progress: false",
|
|
123527
|
+
"jobs:",
|
|
123528
|
+
" gc:",
|
|
123529
|
+
" runs-on: ubuntu-latest",
|
|
123530
|
+
" if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
123531
|
+
" steps:",
|
|
123532
|
+
" - uses: actions/checkout@v5",
|
|
123533
|
+
" with:",
|
|
123534
|
+
" fetch-depth: 0",
|
|
123535
|
+
" - name: Run Postman preview GC (provider-neutral cli.cjs gc)",
|
|
123536
|
+
" env:",
|
|
123537
|
+
" POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}",
|
|
123538
|
+
" POSTMAN_ACCESS_TOKEN: ${{ secrets.POSTMAN_ACCESS_TOKEN }}",
|
|
123539
|
+
" POSTMAN_WORKSPACE_ID: ${{ vars.POSTMAN_WORKSPACE_ID }}",
|
|
123540
|
+
" REPO_URL: https://github.com/${{ github.repository }}",
|
|
123541
|
+
" run: |",
|
|
123542
|
+
" set -euo pipefail",
|
|
123543
|
+
" # The repo-sync action bundles a provider-neutral gc command (cli.cjs gc)",
|
|
123544
|
+
" # that uses the Postman access token for inventory/deletion and the",
|
|
123545
|
+
" # provider ambient credential (GITHUB_TOKEN via git ls-remote) for branch existence.",
|
|
123546
|
+
" # Daily scheduled run is the retention executor (TTL contract of last resort).",
|
|
123547
|
+
' if [ -n "${{ inputs.branch }}" ]; then',
|
|
123548
|
+
' npx @postman-cse/onboarding-repo-sync gc --branch "${{ inputs.branch }}" --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
123549
|
+
" else",
|
|
123550
|
+
' npx @postman-cse/onboarding-repo-sync gc --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
123551
|
+
" fi",
|
|
123552
|
+
" - name: Orphan audit summary (job summary)",
|
|
123553
|
+
" if: always()",
|
|
123554
|
+
" run: |",
|
|
123555
|
+
' echo "### Preview GC orphan audit" >> "$GITHUB_STEP_SUMMARY"',
|
|
123556
|
+
' echo "Marker-guarded deletion only \u2014 strangers (no marker) are never deleted. See gc-summary-json for structured counts." >> "$GITHUB_STEP_SUMMARY"',
|
|
123557
|
+
""
|
|
123558
|
+
].join("\n");
|
|
123559
|
+
}
|
|
123560
|
+
var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
|
|
123506
123561
|
function getCiWorkflowTemplate(provider, options = {}) {
|
|
123507
123562
|
if (provider === "azure-devops") {
|
|
123508
123563
|
const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
|
|
@@ -125073,6 +125128,53 @@ function createInternalIntegrationAdapter(options) {
|
|
|
125073
125128
|
return new BifrostInternalIntegrationAdapter(options);
|
|
125074
125129
|
}
|
|
125075
125130
|
|
|
125131
|
+
// src/lib/retry.ts
|
|
125132
|
+
function sleep(delayMs) {
|
|
125133
|
+
return new Promise((resolve2) => {
|
|
125134
|
+
setTimeout(resolve2, delayMs);
|
|
125135
|
+
});
|
|
125136
|
+
}
|
|
125137
|
+
function normalizeRetryOptions(options) {
|
|
125138
|
+
return {
|
|
125139
|
+
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
125140
|
+
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
125141
|
+
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
125142
|
+
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
125143
|
+
onRetry: options.onRetry ?? (async () => void 0),
|
|
125144
|
+
shouldRetry: options.shouldRetry ?? (() => true),
|
|
125145
|
+
sleep: options.sleep ?? sleep
|
|
125146
|
+
};
|
|
125147
|
+
}
|
|
125148
|
+
async function retry(operation, options = {}) {
|
|
125149
|
+
const normalized = normalizeRetryOptions(options);
|
|
125150
|
+
let nextDelayMs = normalized.delayMs;
|
|
125151
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
125152
|
+
try {
|
|
125153
|
+
return await operation();
|
|
125154
|
+
} catch (error) {
|
|
125155
|
+
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error, {
|
|
125156
|
+
attempt,
|
|
125157
|
+
maxAttempts: normalized.maxAttempts
|
|
125158
|
+
});
|
|
125159
|
+
if (!shouldRetry) {
|
|
125160
|
+
throw error;
|
|
125161
|
+
}
|
|
125162
|
+
await normalized.onRetry({
|
|
125163
|
+
attempt,
|
|
125164
|
+
maxAttempts: normalized.maxAttempts,
|
|
125165
|
+
delayMs: nextDelayMs,
|
|
125166
|
+
error
|
|
125167
|
+
});
|
|
125168
|
+
await normalized.sleep(nextDelayMs);
|
|
125169
|
+
nextDelayMs = Math.min(
|
|
125170
|
+
normalized.maxDelayMs,
|
|
125171
|
+
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
125172
|
+
);
|
|
125173
|
+
}
|
|
125174
|
+
}
|
|
125175
|
+
throw new Error("Retry exhausted without returning or throwing");
|
|
125176
|
+
}
|
|
125177
|
+
|
|
125076
125178
|
// src/contracts.ts
|
|
125077
125179
|
var postmanRepoSyncActionContract = {
|
|
125078
125180
|
name: "postman-repo-sync-action",
|
|
@@ -125242,6 +125344,25 @@ var postmanRepoSyncActionContract = {
|
|
|
125242
125344
|
default: "warn",
|
|
125243
125345
|
allowedValues: ["enforce", "warn"]
|
|
125244
125346
|
},
|
|
125347
|
+
"branch-strategy": {
|
|
125348
|
+
description: "Branch-aware sync strategy. legacy (default) keeps branch-blind behavior; publish-gate restricts canonical writes to the canonical branch and skips repo-sync on other branches; preview additionally maintains suffixed per-branch preview asset sets.",
|
|
125349
|
+
required: false,
|
|
125350
|
+
default: "legacy",
|
|
125351
|
+
allowedValues: ["legacy", "preview", "publish-gate"]
|
|
125352
|
+
},
|
|
125353
|
+
"canonical-branch": {
|
|
125354
|
+
description: "Explicit canonical branch (the sole writer of canonical assets and tracked state). Defaults to the provider-resolved default branch; required on providers without a default-branch variable (Bitbucket, Azure DevOps) when branch-strategy is not legacy.",
|
|
125355
|
+
required: false
|
|
125356
|
+
},
|
|
125357
|
+
"channels": {
|
|
125358
|
+
description: 'Comma-separated channel map for long-lived promotion branches, e.g. "develop=DEV, staging=STAGE, release/*=RC". Channel branches maintain prefix-named parallel asset sets and never mutate canonical assets.',
|
|
125359
|
+
required: false
|
|
125360
|
+
},
|
|
125361
|
+
"preview-ttl": {
|
|
125362
|
+
description: "Sliding TTL in days for preview asset sets (refreshed on every successful preview sync; the retention contract of last resort when no provider credential is available for branch-existence checks).",
|
|
125363
|
+
required: false,
|
|
125364
|
+
default: "30"
|
|
125365
|
+
},
|
|
125245
125366
|
"github-token": {
|
|
125246
125367
|
description: "GitHub token used for repo variable persistence and commits.",
|
|
125247
125368
|
required: false
|
|
@@ -125279,6 +125400,11 @@ var postmanRepoSyncActionContract = {
|
|
|
125279
125400
|
description: "Spec UID from bootstrap, persisted into .postman/resources.yaml cloudResources.",
|
|
125280
125401
|
required: false
|
|
125281
125402
|
},
|
|
125403
|
+
"spec-content-changed": {
|
|
125404
|
+
description: "Whether bootstrap changed canonical spec content; controls final native Spec Hub tag publication.",
|
|
125405
|
+
required: false,
|
|
125406
|
+
default: "true"
|
|
125407
|
+
},
|
|
125282
125408
|
"spec-path": {
|
|
125283
125409
|
description: "Optional repo-root-relative path to the local spec file for resources/workflows metadata.",
|
|
125284
125410
|
required: false
|
|
@@ -125323,6 +125449,18 @@ var postmanRepoSyncActionContract = {
|
|
|
125323
125449
|
},
|
|
125324
125450
|
"commit-sha": {
|
|
125325
125451
|
description: "Commit SHA produced by repo-write-mode, if any."
|
|
125452
|
+
},
|
|
125453
|
+
"sync-status": {
|
|
125454
|
+
description: "Branch-aware sync status: synced, skipped-branch-gate, or empty under branch-strategy legacy."
|
|
125455
|
+
},
|
|
125456
|
+
"branch-decision": {
|
|
125457
|
+
description: "Serialized BranchDecision JSON for downstream actions (also exported as POSTMAN_BRANCH_DECISION)."
|
|
125458
|
+
},
|
|
125459
|
+
"spec-version-tag": {
|
|
125460
|
+
description: "Native Spec Hub version tag created after successful canonical repo-sync finalization."
|
|
125461
|
+
},
|
|
125462
|
+
"spec-version-url": {
|
|
125463
|
+
description: "Read-only URL for the tagged Spec Hub snapshot."
|
|
125326
125464
|
}
|
|
125327
125465
|
},
|
|
125328
125466
|
behavior: {
|
|
@@ -125390,53 +125528,6 @@ var PostmanAssetsClient = class {
|
|
|
125390
125528
|
}
|
|
125391
125529
|
};
|
|
125392
125530
|
|
|
125393
|
-
// src/lib/retry.ts
|
|
125394
|
-
function sleep(delayMs) {
|
|
125395
|
-
return new Promise((resolve2) => {
|
|
125396
|
-
setTimeout(resolve2, delayMs);
|
|
125397
|
-
});
|
|
125398
|
-
}
|
|
125399
|
-
function normalizeRetryOptions(options) {
|
|
125400
|
-
return {
|
|
125401
|
-
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
125402
|
-
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
125403
|
-
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
125404
|
-
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
125405
|
-
onRetry: options.onRetry ?? (async () => void 0),
|
|
125406
|
-
shouldRetry: options.shouldRetry ?? (() => true),
|
|
125407
|
-
sleep: options.sleep ?? sleep
|
|
125408
|
-
};
|
|
125409
|
-
}
|
|
125410
|
-
async function retry(operation, options = {}) {
|
|
125411
|
-
const normalized = normalizeRetryOptions(options);
|
|
125412
|
-
let nextDelayMs = normalized.delayMs;
|
|
125413
|
-
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
125414
|
-
try {
|
|
125415
|
-
return await operation();
|
|
125416
|
-
} catch (error) {
|
|
125417
|
-
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error, {
|
|
125418
|
-
attempt,
|
|
125419
|
-
maxAttempts: normalized.maxAttempts
|
|
125420
|
-
});
|
|
125421
|
-
if (!shouldRetry) {
|
|
125422
|
-
throw error;
|
|
125423
|
-
}
|
|
125424
|
-
await normalized.onRetry({
|
|
125425
|
-
attempt,
|
|
125426
|
-
maxAttempts: normalized.maxAttempts,
|
|
125427
|
-
delayMs: nextDelayMs,
|
|
125428
|
-
error
|
|
125429
|
-
});
|
|
125430
|
-
await normalized.sleep(nextDelayMs);
|
|
125431
|
-
nextDelayMs = Math.min(
|
|
125432
|
-
normalized.maxDelayMs,
|
|
125433
|
-
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
125434
|
-
);
|
|
125435
|
-
}
|
|
125436
|
-
}
|
|
125437
|
-
throw new Error("Retry exhausted without returning or throwing");
|
|
125438
|
-
}
|
|
125439
|
-
|
|
125440
125531
|
// src/lib/postman/postman-gateway-assets-client.ts
|
|
125441
125532
|
var MAX_CREATE_FLIGHTS = 256;
|
|
125442
125533
|
var createFlights = /* @__PURE__ */ new Map();
|
|
@@ -125462,6 +125553,84 @@ var PostmanGatewayAssetsClient = class {
|
|
|
125462
125553
|
if (!envelope) return null;
|
|
125463
125554
|
return this.asRecord(envelope.data) ?? envelope;
|
|
125464
125555
|
}
|
|
125556
|
+
/** Native Spec Hub tags attach to the latest changelog group. */
|
|
125557
|
+
async tagSpecVersion(specId, name) {
|
|
125558
|
+
const trimmed = name.trim().slice(0, 255);
|
|
125559
|
+
const response = await this.gateway.requestJson({
|
|
125560
|
+
service: "specification",
|
|
125561
|
+
method: "post",
|
|
125562
|
+
path: `/specifications/${specId}/tags`,
|
|
125563
|
+
body: { name: trimmed }
|
|
125564
|
+
});
|
|
125565
|
+
const record = this.dataOf(response) ?? {};
|
|
125566
|
+
return { id: String(record.id ?? "").trim(), name: String(record.name ?? trimmed).trim() };
|
|
125567
|
+
}
|
|
125568
|
+
async listSpecVersionTags(specId) {
|
|
125569
|
+
const response = await this.gateway.requestJson({
|
|
125570
|
+
service: "specification",
|
|
125571
|
+
method: "get",
|
|
125572
|
+
path: `/specifications/${specId}/tags`,
|
|
125573
|
+
query: { limit: "50" }
|
|
125574
|
+
});
|
|
125575
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
125576
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({
|
|
125577
|
+
id: String(entry.id ?? "").trim(),
|
|
125578
|
+
// listTags returns `message`; createTag returns `name`. Accept both.
|
|
125579
|
+
name: String(entry.name ?? entry.message ?? "").trim()
|
|
125580
|
+
})).filter((entry) => entry.id || entry.name);
|
|
125581
|
+
}
|
|
125582
|
+
async deleteCollection(collectionUid) {
|
|
125583
|
+
const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
|
|
125584
|
+
await this.gateway.requestJson({
|
|
125585
|
+
service: "collection",
|
|
125586
|
+
method: "delete",
|
|
125587
|
+
path: `/v3/collections/${bareId}`
|
|
125588
|
+
});
|
|
125589
|
+
}
|
|
125590
|
+
async listSpecifications(workspaceId) {
|
|
125591
|
+
const response = await this.gateway.requestJson({
|
|
125592
|
+
service: "specification",
|
|
125593
|
+
method: "get",
|
|
125594
|
+
path: `/specifications?containerType=workspace&containerId=${workspaceId}`
|
|
125595
|
+
});
|
|
125596
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
125597
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({ uid: String(entry.id ?? entry.uid ?? "").trim(), name: String(entry.name ?? "").trim() })).filter((entry) => entry.uid && entry.name);
|
|
125598
|
+
}
|
|
125599
|
+
async getSpecContent(specId) {
|
|
125600
|
+
const files = await this.gateway.requestJson({
|
|
125601
|
+
service: "specification",
|
|
125602
|
+
method: "get",
|
|
125603
|
+
path: `/specifications/${specId}/files`
|
|
125604
|
+
});
|
|
125605
|
+
const entries = Array.isArray(files?.data) ? files.data : [];
|
|
125606
|
+
const root = entries.map((entry) => this.asRecord(entry)).find((entry) => entry?.type === "ROOT") ?? entries.map((entry) => this.asRecord(entry)).find((entry) => entry !== null);
|
|
125607
|
+
const fileId = String(root?.id ?? "").trim();
|
|
125608
|
+
if (!fileId) return void 0;
|
|
125609
|
+
const file = await this.gateway.requestJson({
|
|
125610
|
+
service: "specification",
|
|
125611
|
+
method: "get",
|
|
125612
|
+
path: `/specifications/${specId}/files/${fileId}`,
|
|
125613
|
+
query: { fields: "content" }
|
|
125614
|
+
});
|
|
125615
|
+
const record = this.dataOf(file);
|
|
125616
|
+
return typeof record?.content === "string" ? record.content : void 0;
|
|
125617
|
+
}
|
|
125618
|
+
async listSpecCollections(specId) {
|
|
125619
|
+
const response = await this.gateway.requestJson({
|
|
125620
|
+
service: "specification",
|
|
125621
|
+
method: "get",
|
|
125622
|
+
path: `/specifications/${specId}/collections`
|
|
125623
|
+
});
|
|
125624
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
125625
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({ uid: String(entry.collection ?? entry.collectionId ?? entry.id ?? "").trim(), name: String(entry.name ?? "").trim() })).filter((entry) => entry.uid);
|
|
125626
|
+
}
|
|
125627
|
+
async deleteSpec(specId) {
|
|
125628
|
+
await this.gateway.requestJson({
|
|
125629
|
+
service: "specification",
|
|
125630
|
+
method: "delete",
|
|
125631
|
+
path: `/specifications/${specId}`
|
|
125632
|
+
});
|
|
125633
|
+
}
|
|
125465
125634
|
idOf(record) {
|
|
125466
125635
|
if (!record) return "";
|
|
125467
125636
|
const id = record.uid ?? record.id;
|
|
@@ -125746,6 +125915,9 @@ var PostmanGatewayAssetsClient = class {
|
|
|
125746
125915
|
path: `/mocks?workspace=${ws}`,
|
|
125747
125916
|
body
|
|
125748
125917
|
},
|
|
125918
|
+
// Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
|
|
125919
|
+
// may still have created the mock; reconcile via discovery below and
|
|
125920
|
+
// let the orchestrator retry the whole create-or-adopt cycle.
|
|
125749
125921
|
{ retryTransient: false }
|
|
125750
125922
|
);
|
|
125751
125923
|
const record = this.dataOf(response);
|
|
@@ -125784,6 +125956,33 @@ var PostmanGatewayAssetsClient = class {
|
|
|
125784
125956
|
environment: String(m.environment ?? "")
|
|
125785
125957
|
}));
|
|
125786
125958
|
}
|
|
125959
|
+
/**
|
|
125960
|
+
* Delete an environment through the sync service (GC path). The path id is
|
|
125961
|
+
* the bare model id (public uid tail), mirroring updateEnvironment.
|
|
125962
|
+
*/
|
|
125963
|
+
async deleteEnvironment(uid) {
|
|
125964
|
+
await this.gateway.requestJson({
|
|
125965
|
+
service: "sync",
|
|
125966
|
+
method: "delete",
|
|
125967
|
+
path: `/environment/${this.toModelId(uid)}`
|
|
125968
|
+
});
|
|
125969
|
+
}
|
|
125970
|
+
/** Delete a mock server (GC path). */
|
|
125971
|
+
async deleteMock(uid) {
|
|
125972
|
+
await this.gateway.requestJson({
|
|
125973
|
+
service: "mock",
|
|
125974
|
+
method: "delete",
|
|
125975
|
+
path: `/mocks/${this.toModelId(uid)}`
|
|
125976
|
+
});
|
|
125977
|
+
}
|
|
125978
|
+
/** Delete a collection-based monitor (jobTemplate) (GC path). */
|
|
125979
|
+
async deleteMonitor(uid) {
|
|
125980
|
+
await this.gateway.requestJson({
|
|
125981
|
+
service: "monitors",
|
|
125982
|
+
method: "delete",
|
|
125983
|
+
path: `/jobTemplates/${this.toModelId(uid)}`
|
|
125984
|
+
});
|
|
125985
|
+
}
|
|
125787
125986
|
async mockExists(uid) {
|
|
125788
125987
|
try {
|
|
125789
125988
|
await this.gateway.requestJson({
|
|
@@ -126238,6 +126437,331 @@ var AccessTokenGatewayClient = class {
|
|
|
126238
126437
|
}
|
|
126239
126438
|
};
|
|
126240
126439
|
|
|
126440
|
+
// src/lib/repo/branch-decision.ts
|
|
126441
|
+
var import_node_fs3 = require("node:fs");
|
|
126442
|
+
var import_node_crypto2 = require("node:crypto");
|
|
126443
|
+
var ContractError = class extends Error {
|
|
126444
|
+
code;
|
|
126445
|
+
constructor(code, message) {
|
|
126446
|
+
super(`${code}: ${message}`);
|
|
126447
|
+
this.code = code;
|
|
126448
|
+
this.name = "ContractError";
|
|
126449
|
+
}
|
|
126450
|
+
};
|
|
126451
|
+
function clean(value) {
|
|
126452
|
+
const trimmed = (value ?? "").trim();
|
|
126453
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
126454
|
+
}
|
|
126455
|
+
function stripRefPrefix(ref) {
|
|
126456
|
+
const raw = clean(ref);
|
|
126457
|
+
if (!raw) {
|
|
126458
|
+
return { kind: "unknown" };
|
|
126459
|
+
}
|
|
126460
|
+
if (raw.startsWith("refs/heads/")) {
|
|
126461
|
+
return { name: raw.slice("refs/heads/".length), kind: "branch" };
|
|
126462
|
+
}
|
|
126463
|
+
if (raw.startsWith("refs/tags/")) {
|
|
126464
|
+
return { name: raw.slice("refs/tags/".length), kind: "tag" };
|
|
126465
|
+
}
|
|
126466
|
+
if (raw.startsWith("refs/pull/") || raw.startsWith("refs/merge")) {
|
|
126467
|
+
return { kind: "unknown" };
|
|
126468
|
+
}
|
|
126469
|
+
return { name: raw, kind: "branch" };
|
|
126470
|
+
}
|
|
126471
|
+
function detectProvider(env) {
|
|
126472
|
+
if (clean(env.GITHUB_ACTIONS) || clean(env.GITHUB_REPOSITORY)) return "github";
|
|
126473
|
+
if (clean(env.GITLAB_CI) || clean(env.CI_PROJECT_PATH)) return "gitlab";
|
|
126474
|
+
if (clean(env.BITBUCKET_REPO_SLUG) || clean(env.BITBUCKET_BRANCH)) return "bitbucket";
|
|
126475
|
+
if (clean(env.TF_BUILD) || clean(env.BUILD_REPOSITORY_URI)) return "azure-devops";
|
|
126476
|
+
return "unknown";
|
|
126477
|
+
}
|
|
126478
|
+
function readGithubEvent(env) {
|
|
126479
|
+
const path5 = clean(env.GITHUB_EVENT_PATH);
|
|
126480
|
+
if (!path5) return void 0;
|
|
126481
|
+
try {
|
|
126482
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(path5, "utf8"));
|
|
126483
|
+
} catch {
|
|
126484
|
+
return void 0;
|
|
126485
|
+
}
|
|
126486
|
+
}
|
|
126487
|
+
function resolveBranchIdentity(env = process.env, overrides = {}) {
|
|
126488
|
+
const provider = detectProvider(env);
|
|
126489
|
+
const explicitDefault = clean(overrides.defaultBranch);
|
|
126490
|
+
let headBranch;
|
|
126491
|
+
let rawRef;
|
|
126492
|
+
let refKind;
|
|
126493
|
+
let isPrContext = false;
|
|
126494
|
+
let isForkPr = false;
|
|
126495
|
+
let defaultBranch = explicitDefault;
|
|
126496
|
+
let headSha;
|
|
126497
|
+
switch (provider) {
|
|
126498
|
+
case "github": {
|
|
126499
|
+
const event = readGithubEvent(env);
|
|
126500
|
+
headSha = clean(env.GITHUB_SHA);
|
|
126501
|
+
defaultBranch ??= clean(event?.repository?.default_branch);
|
|
126502
|
+
const headRef = clean(env.GITHUB_HEAD_REF);
|
|
126503
|
+
if (headRef) {
|
|
126504
|
+
isPrContext = true;
|
|
126505
|
+
headBranch = headRef;
|
|
126506
|
+
rawRef = clean(env.GITHUB_REF) ?? headRef;
|
|
126507
|
+
refKind = "branch";
|
|
126508
|
+
const headRepo = event?.pull_request?.head?.repo?.full_name;
|
|
126509
|
+
const baseRepo = event?.pull_request?.base?.repo?.full_name ?? event?.repository?.full_name;
|
|
126510
|
+
isForkPr = Boolean(headRepo && baseRepo && headRepo !== baseRepo);
|
|
126511
|
+
headSha = clean(event?.pull_request?.head?.sha) ?? headSha;
|
|
126512
|
+
} else {
|
|
126513
|
+
rawRef = clean(env.GITHUB_REF) ?? clean(env.GITHUB_REF_NAME);
|
|
126514
|
+
const parsed = stripRefPrefix(rawRef);
|
|
126515
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
126516
|
+
refKind = parsed.kind;
|
|
126517
|
+
}
|
|
126518
|
+
break;
|
|
126519
|
+
}
|
|
126520
|
+
case "gitlab": {
|
|
126521
|
+
headSha = clean(env.CI_COMMIT_SHA);
|
|
126522
|
+
defaultBranch ??= clean(env.CI_DEFAULT_BRANCH);
|
|
126523
|
+
const mrSource = clean(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME);
|
|
126524
|
+
if (mrSource) {
|
|
126525
|
+
isPrContext = true;
|
|
126526
|
+
headBranch = mrSource;
|
|
126527
|
+
rawRef = mrSource;
|
|
126528
|
+
refKind = "branch";
|
|
126529
|
+
const sourceProject = clean(env.CI_MERGE_REQUEST_SOURCE_PROJECT_ID);
|
|
126530
|
+
const targetProject = clean(env.CI_MERGE_REQUEST_PROJECT_ID);
|
|
126531
|
+
isForkPr = Boolean(sourceProject && targetProject && sourceProject !== targetProject);
|
|
126532
|
+
} else if (clean(env.CI_COMMIT_TAG)) {
|
|
126533
|
+
rawRef = clean(env.CI_COMMIT_TAG);
|
|
126534
|
+
refKind = "tag";
|
|
126535
|
+
} else {
|
|
126536
|
+
headBranch = clean(env.CI_COMMIT_BRANCH) ?? clean(env.CI_COMMIT_REF_NAME);
|
|
126537
|
+
rawRef = headBranch;
|
|
126538
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
126539
|
+
}
|
|
126540
|
+
break;
|
|
126541
|
+
}
|
|
126542
|
+
case "bitbucket": {
|
|
126543
|
+
headSha = clean(env.BITBUCKET_COMMIT);
|
|
126544
|
+
if (clean(env.BITBUCKET_TAG)) {
|
|
126545
|
+
rawRef = clean(env.BITBUCKET_TAG);
|
|
126546
|
+
refKind = "tag";
|
|
126547
|
+
} else {
|
|
126548
|
+
headBranch = clean(env.BITBUCKET_BRANCH);
|
|
126549
|
+
rawRef = headBranch;
|
|
126550
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
126551
|
+
isPrContext = Boolean(clean(env.BITBUCKET_PR_ID));
|
|
126552
|
+
}
|
|
126553
|
+
break;
|
|
126554
|
+
}
|
|
126555
|
+
case "azure-devops": {
|
|
126556
|
+
headSha = clean(env.BUILD_SOURCEVERSION);
|
|
126557
|
+
const prSource = clean(env.SYSTEM_PULLREQUEST_SOURCEBRANCH);
|
|
126558
|
+
if (prSource) {
|
|
126559
|
+
isPrContext = true;
|
|
126560
|
+
const parsed = stripRefPrefix(prSource);
|
|
126561
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
126562
|
+
rawRef = prSource;
|
|
126563
|
+
refKind = parsed.kind;
|
|
126564
|
+
const forkFlag = clean(env.SYSTEM_PULLREQUEST_ISFORK);
|
|
126565
|
+
isForkPr = forkFlag?.toLowerCase() === "true";
|
|
126566
|
+
} else {
|
|
126567
|
+
rawRef = clean(env.BUILD_SOURCEBRANCH);
|
|
126568
|
+
const parsed = stripRefPrefix(rawRef);
|
|
126569
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
126570
|
+
refKind = parsed.kind;
|
|
126571
|
+
}
|
|
126572
|
+
break;
|
|
126573
|
+
}
|
|
126574
|
+
default: {
|
|
126575
|
+
refKind = "unknown";
|
|
126576
|
+
}
|
|
126577
|
+
}
|
|
126578
|
+
if (refKind === "branch" && headBranch && defaultBranch && headBranch === defaultBranch) {
|
|
126579
|
+
refKind = "default-branch";
|
|
126580
|
+
}
|
|
126581
|
+
return { provider, headBranch, rawRef, defaultBranch, refKind, isPrContext, isForkPr, headSha };
|
|
126582
|
+
}
|
|
126583
|
+
function parseChannelRules(input) {
|
|
126584
|
+
const raw = clean(input);
|
|
126585
|
+
const rules = [];
|
|
126586
|
+
for (const part of raw ? raw.split(",") : []) {
|
|
126587
|
+
const entry = part.trim();
|
|
126588
|
+
if (!entry) continue;
|
|
126589
|
+
const eq = entry.indexOf("=");
|
|
126590
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
126591
|
+
throw new ContractError(
|
|
126592
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
126593
|
+
`channels entry "${entry}" must be <branch-or-glob>=<CODE>`
|
|
126594
|
+
);
|
|
126595
|
+
}
|
|
126596
|
+
const pattern = entry.slice(0, eq).trim();
|
|
126597
|
+
const code = entry.slice(eq + 1).trim().toUpperCase();
|
|
126598
|
+
if (!/^[A-Z][A-Z0-9_-]{0,15}$/.test(code)) {
|
|
126599
|
+
throw new ContractError(
|
|
126600
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
126601
|
+
`channel code "${code}" must be 1-16 chars, A-Z 0-9 _ -, starting with a letter`
|
|
126602
|
+
);
|
|
126603
|
+
}
|
|
126604
|
+
rules.push({ pattern, code });
|
|
126605
|
+
}
|
|
126606
|
+
if (!rules.some((rule) => rule.pattern === "release/*")) {
|
|
126607
|
+
rules.push({ pattern: "release/*", code: "RC" });
|
|
126608
|
+
}
|
|
126609
|
+
return rules;
|
|
126610
|
+
}
|
|
126611
|
+
function matchChannel(branch, rules) {
|
|
126612
|
+
for (const rule of rules) {
|
|
126613
|
+
if (rule.pattern.endsWith("*")) {
|
|
126614
|
+
const prefix = rule.pattern.slice(0, -1);
|
|
126615
|
+
if (branch.startsWith(prefix)) return rule;
|
|
126616
|
+
} else if (branch === rule.pattern) {
|
|
126617
|
+
return rule;
|
|
126618
|
+
}
|
|
126619
|
+
}
|
|
126620
|
+
return void 0;
|
|
126621
|
+
}
|
|
126622
|
+
function resolveBranchDecision(options) {
|
|
126623
|
+
const { strategy, identity } = options;
|
|
126624
|
+
const channels = options.channels ?? [];
|
|
126625
|
+
if (strategy === "legacy") {
|
|
126626
|
+
return {
|
|
126627
|
+
tier: "legacy",
|
|
126628
|
+
strategy,
|
|
126629
|
+
identity,
|
|
126630
|
+
canonicalBranch: clean(options.canonicalBranch) ?? identity.defaultBranch,
|
|
126631
|
+
reason: "branch-strategy legacy: branch-blind pre-v2 behavior"
|
|
126632
|
+
};
|
|
126633
|
+
}
|
|
126634
|
+
const canonicalBranch = clean(options.canonicalBranch) ?? identity.defaultBranch;
|
|
126635
|
+
if (!canonicalBranch) {
|
|
126636
|
+
throw new ContractError(
|
|
126637
|
+
"CONTRACT_DEFAULT_BRANCH_UNRESOLVED",
|
|
126638
|
+
`cannot resolve the canonical branch on ${identity.provider} (no explicit canonical-branch input and the provider exposes no default-branch variable). Set the canonical-branch input.`
|
|
126639
|
+
);
|
|
126640
|
+
}
|
|
126641
|
+
if (identity.refKind === "tag" || identity.refKind === "unknown" || !identity.headBranch) {
|
|
126642
|
+
return {
|
|
126643
|
+
tier: "gated",
|
|
126644
|
+
strategy,
|
|
126645
|
+
identity,
|
|
126646
|
+
canonicalBranch,
|
|
126647
|
+
reason: `ref kind ${identity.refKind}: never canonical/preview-eligible; no-op with annotation`
|
|
126648
|
+
};
|
|
126649
|
+
}
|
|
126650
|
+
if (identity.headBranch === canonicalBranch) {
|
|
126651
|
+
return {
|
|
126652
|
+
tier: "canonical",
|
|
126653
|
+
strategy,
|
|
126654
|
+
identity,
|
|
126655
|
+
canonicalBranch,
|
|
126656
|
+
reason: `head branch equals canonical branch ${canonicalBranch}`
|
|
126657
|
+
};
|
|
126658
|
+
}
|
|
126659
|
+
const channel = matchChannel(identity.headBranch, channels);
|
|
126660
|
+
if (channel) {
|
|
126661
|
+
return {
|
|
126662
|
+
tier: "channel",
|
|
126663
|
+
strategy,
|
|
126664
|
+
identity,
|
|
126665
|
+
canonicalBranch,
|
|
126666
|
+
channel,
|
|
126667
|
+
reason: `branch ${identity.headBranch} matches channel ${channel.pattern}=${channel.code}`
|
|
126668
|
+
};
|
|
126669
|
+
}
|
|
126670
|
+
if (strategy === "preview") {
|
|
126671
|
+
if (identity.isForkPr) {
|
|
126672
|
+
return {
|
|
126673
|
+
tier: "gated",
|
|
126674
|
+
strategy,
|
|
126675
|
+
identity,
|
|
126676
|
+
canonicalBranch,
|
|
126677
|
+
reason: "fork PR: preview-ineligible (same-repo gate), gated instead"
|
|
126678
|
+
};
|
|
126679
|
+
}
|
|
126680
|
+
return {
|
|
126681
|
+
tier: "preview",
|
|
126682
|
+
strategy,
|
|
126683
|
+
identity,
|
|
126684
|
+
canonicalBranch,
|
|
126685
|
+
reason: `branch ${identity.headBranch} under branch-strategy preview`
|
|
126686
|
+
};
|
|
126687
|
+
}
|
|
126688
|
+
return {
|
|
126689
|
+
tier: "gated",
|
|
126690
|
+
strategy,
|
|
126691
|
+
identity,
|
|
126692
|
+
canonicalBranch,
|
|
126693
|
+
reason: `branch ${identity.headBranch} under branch-strategy publish-gate`
|
|
126694
|
+
};
|
|
126695
|
+
}
|
|
126696
|
+
var BRANCH_DECISION_ENV = "POSTMAN_BRANCH_DECISION";
|
|
126697
|
+
function serializeBranchDecision(decision) {
|
|
126698
|
+
return JSON.stringify(decision);
|
|
126699
|
+
}
|
|
126700
|
+
function parseBranchDecision(raw) {
|
|
126701
|
+
const value = clean(raw);
|
|
126702
|
+
if (!value) return void 0;
|
|
126703
|
+
let parsed;
|
|
126704
|
+
try {
|
|
126705
|
+
parsed = JSON.parse(value);
|
|
126706
|
+
} catch {
|
|
126707
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION is not valid JSON");
|
|
126708
|
+
}
|
|
126709
|
+
const candidate = parsed;
|
|
126710
|
+
const tiers = ["canonical", "channel", "preview", "gated", "legacy"];
|
|
126711
|
+
if (!candidate || typeof candidate !== "object" || !tiers.includes(candidate.tier) || !candidate.identity) {
|
|
126712
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION does not carry a valid BranchDecision");
|
|
126713
|
+
}
|
|
126714
|
+
return candidate;
|
|
126715
|
+
}
|
|
126716
|
+
function resolveEffectiveBranchDecision(options, env = process.env) {
|
|
126717
|
+
const inherited = parseBranchDecision(env[BRANCH_DECISION_ENV]);
|
|
126718
|
+
if (inherited) return inherited;
|
|
126719
|
+
return resolveBranchDecision(options);
|
|
126720
|
+
}
|
|
126721
|
+
var PREVIEW_SLUG_MAX = 30;
|
|
126722
|
+
function buildBranchSlug(rawBranch) {
|
|
126723
|
+
const sanitized = rawBranch.replace(/^refs\/heads\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
126724
|
+
const truncated = sanitized.slice(0, PREVIEW_SLUG_MAX);
|
|
126725
|
+
const lossy = truncated !== rawBranch.replace(/^refs\/heads\//, "");
|
|
126726
|
+
if (!lossy) {
|
|
126727
|
+
return { suffix: truncated, slug: truncated, lossy };
|
|
126728
|
+
}
|
|
126729
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(rawBranch).digest("hex").slice(0, 6);
|
|
126730
|
+
return { suffix: `${truncated}-${hash}`, slug: truncated, lossy };
|
|
126731
|
+
}
|
|
126732
|
+
function previewAssetName(baseName, rawBranch) {
|
|
126733
|
+
return `${baseName} @${buildBranchSlug(rawBranch).suffix}`;
|
|
126734
|
+
}
|
|
126735
|
+
function channelAssetName(baseName, code) {
|
|
126736
|
+
return `[${code}] ${baseName}`;
|
|
126737
|
+
}
|
|
126738
|
+
var MARKER_KEY = "x-pm-onboarding";
|
|
126739
|
+
function parseAssetMarker(description) {
|
|
126740
|
+
if (!description) return void 0;
|
|
126741
|
+
const index = description.indexOf(`${MARKER_KEY}:`);
|
|
126742
|
+
if (index === -1) return void 0;
|
|
126743
|
+
const jsonStart = description.indexOf("{", index);
|
|
126744
|
+
if (jsonStart === -1) return void 0;
|
|
126745
|
+
let depth = 0;
|
|
126746
|
+
for (let i = jsonStart; i < description.length; i += 1) {
|
|
126747
|
+
const ch = description[i];
|
|
126748
|
+
if (ch === "{") depth += 1;
|
|
126749
|
+
else if (ch === "}") {
|
|
126750
|
+
depth -= 1;
|
|
126751
|
+
if (depth === 0) {
|
|
126752
|
+
try {
|
|
126753
|
+
const parsed = JSON.parse(description.slice(jsonStart, i + 1));
|
|
126754
|
+
if (parsed && typeof parsed === "object" && parsed.repo && parsed.role) return parsed;
|
|
126755
|
+
} catch {
|
|
126756
|
+
return void 0;
|
|
126757
|
+
}
|
|
126758
|
+
return void 0;
|
|
126759
|
+
}
|
|
126760
|
+
}
|
|
126761
|
+
}
|
|
126762
|
+
return void 0;
|
|
126763
|
+
}
|
|
126764
|
+
|
|
126241
126765
|
// src/index.ts
|
|
126242
126766
|
function parseBooleanInput(value, defaultValue) {
|
|
126243
126767
|
const normalized = String(value || "").trim().toLowerCase();
|
|
@@ -126256,16 +126780,16 @@ function getInput(name, env = process.env) {
|
|
|
126256
126780
|
const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
|
|
126257
126781
|
const hasNormalized = normalizedRaw !== void 0;
|
|
126258
126782
|
const hasRunner = runnerRaw !== void 0;
|
|
126783
|
+
const normalizedValue = normalizeInputValue(normalizedRaw);
|
|
126784
|
+
const runnerValue = normalizeInputValue(runnerRaw);
|
|
126259
126785
|
if (hasNormalized && hasRunner) {
|
|
126260
|
-
|
|
126261
|
-
const runnerValue = normalizeInputValue(runnerRaw);
|
|
126262
|
-
if (normalizedValue !== runnerValue) {
|
|
126786
|
+
if (normalizedValue && runnerValue && normalizedValue !== runnerValue) {
|
|
126263
126787
|
throw new Error(
|
|
126264
126788
|
`Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
|
|
126265
126789
|
);
|
|
126266
126790
|
}
|
|
126267
126791
|
}
|
|
126268
|
-
return
|
|
126792
|
+
return normalizedValue || runnerValue;
|
|
126269
126793
|
}
|
|
126270
126794
|
function hasInput(name, env = process.env) {
|
|
126271
126795
|
const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
|
|
@@ -126324,6 +126848,17 @@ function parseCredentialPreflight(value) {
|
|
|
126324
126848
|
`Unsupported credential-preflight "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
126325
126849
|
);
|
|
126326
126850
|
}
|
|
126851
|
+
function parseBranchStrategy(value) {
|
|
126852
|
+
const definition = postmanRepoSyncActionContract.inputs["branch-strategy"];
|
|
126853
|
+
const allowed = definition.allowedValues ?? [];
|
|
126854
|
+
const normalized = String(value || "").trim() || (definition.default ?? "legacy");
|
|
126855
|
+
if (allowed.includes(normalized)) {
|
|
126856
|
+
return normalized;
|
|
126857
|
+
}
|
|
126858
|
+
throw new Error(
|
|
126859
|
+
`Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
126860
|
+
);
|
|
126861
|
+
}
|
|
126327
126862
|
function normalizeReleaseLabel(value) {
|
|
126328
126863
|
const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
126329
126864
|
return cleaned;
|
|
@@ -126368,6 +126903,7 @@ function resolveInputs(env = process.env) {
|
|
|
126368
126903
|
smokeCollectionId: getInput("smoke-collection-id", env),
|
|
126369
126904
|
contractCollectionId: getInput("contract-collection-id", env),
|
|
126370
126905
|
specId: getInput("spec-id", env),
|
|
126906
|
+
specContentChanged: parseBooleanInput(getInput("spec-content-changed", env), true),
|
|
126371
126907
|
specPath: getInput("spec-path", env),
|
|
126372
126908
|
collectionSyncMode: normalizeCollectionSyncMode(getInput("collection-sync-mode", env) || "refresh"),
|
|
126373
126909
|
specSyncMode: normalizeSpecSyncMode(getInput("spec-sync-mode", env) || "update"),
|
|
@@ -126390,6 +126926,10 @@ function resolveInputs(env = process.env) {
|
|
|
126390
126926
|
postmanApiKey: getInput("postman-api-key", env),
|
|
126391
126927
|
postmanAccessToken: getInput("postman-access-token", env),
|
|
126392
126928
|
credentialPreflight: parseCredentialPreflight(getInput("credential-preflight", env)),
|
|
126929
|
+
branchStrategy: parseBranchStrategy(getInput("branch-strategy", env)),
|
|
126930
|
+
canonicalBranch: getInput("canonical-branch", env) || void 0,
|
|
126931
|
+
channels: getInput("channels", env) || void 0,
|
|
126932
|
+
previewTtlDays: Math.max(1, Number.parseInt(getInput("preview-ttl", env) || "30", 10) || 30),
|
|
126393
126933
|
adoToken: getInput("ado-token", env) || normalizeInputValue(env.SYSTEM_ACCESSTOKEN),
|
|
126394
126934
|
githubToken: getInput("github-token", env),
|
|
126395
126935
|
ghFallbackToken: getInput("gh-fallback-token", env),
|
|
@@ -126416,6 +126956,40 @@ function resolveInputs(env = process.env) {
|
|
|
126416
126956
|
postmanIapubBase: endpointProfile.iapubBaseUrl
|
|
126417
126957
|
};
|
|
126418
126958
|
}
|
|
126959
|
+
function buildBranchAssetMarker(decision, inputs, now = /* @__PURE__ */ new Date()) {
|
|
126960
|
+
if (decision.tier !== "preview" && decision.tier !== "channel") {
|
|
126961
|
+
return void 0;
|
|
126962
|
+
}
|
|
126963
|
+
const rawBranch = decision.identity.headBranch;
|
|
126964
|
+
const repo = inputs.repoUrl || inputs.repository;
|
|
126965
|
+
if (!rawBranch || !repo) {
|
|
126966
|
+
return void 0;
|
|
126967
|
+
}
|
|
126968
|
+
const ttlMs = inputs.previewTtlDays * 24 * 60 * 60 * 1e3;
|
|
126969
|
+
return {
|
|
126970
|
+
repo,
|
|
126971
|
+
rawBranch,
|
|
126972
|
+
sanitizedBranch: buildBranchSlug(rawBranch).suffix,
|
|
126973
|
+
role: decision.tier,
|
|
126974
|
+
headSha: decision.identity.headSha,
|
|
126975
|
+
createdAt: now.toISOString(),
|
|
126976
|
+
lastSyncedAt: now.toISOString(),
|
|
126977
|
+
...decision.tier === "preview" ? { expiresAt: new Date(now.getTime() + ttlMs).toISOString() } : {}
|
|
126978
|
+
};
|
|
126979
|
+
}
|
|
126980
|
+
function assertBranchAssetIds(inputs, decision, branchOwnedIds = process.env.POSTMAN_BRANCH_ASSET_IDS === "owned") {
|
|
126981
|
+
if (decision.tier === "legacy" || decision.tier === "canonical" || branchOwnedIds) return;
|
|
126982
|
+
const provided = [
|
|
126983
|
+
["baseline-collection-id", inputs.baselineCollectionId],
|
|
126984
|
+
["smoke-collection-id", inputs.smokeCollectionId],
|
|
126985
|
+
["contract-collection-id", inputs.contractCollectionId]
|
|
126986
|
+
].filter(([, value]) => Boolean(value));
|
|
126987
|
+
if (provided.length > 0) {
|
|
126988
|
+
throw new Error(
|
|
126989
|
+
`CONTRACT_BRANCH_CANONICAL_WRITE: a ${decision.tier} repo-sync run cannot accept explicit collection IDs (${provided.map(([name]) => name).join(", ")}). Run bootstrap in the same branch-aware pipeline so it can produce branch-owned IDs.`
|
|
126990
|
+
);
|
|
126991
|
+
}
|
|
126992
|
+
}
|
|
126419
126993
|
function buildEnvironmentValues(envName, baseUrl) {
|
|
126420
126994
|
return [
|
|
126421
126995
|
{ key: "baseUrl", value: baseUrl, type: "default" },
|
|
@@ -126428,12 +127002,48 @@ function buildEnvironmentValues(envName, baseUrl) {
|
|
|
126428
127002
|
];
|
|
126429
127003
|
}
|
|
126430
127004
|
var LEGACY_BASELINE_COLLECTION_PREFIX = "[Baseline]";
|
|
127005
|
+
var RESOURCES_STATE_VERSION = 2;
|
|
127006
|
+
var SUPPORTED_STATE_VERSIONS = /* @__PURE__ */ new Set([1, RESOURCES_STATE_VERSION]);
|
|
127007
|
+
var StateUnreadableError = class extends Error {
|
|
127008
|
+
code = "CONTRACT_STATE_UNREADABLE";
|
|
127009
|
+
constructor(message) {
|
|
127010
|
+
super(`CONTRACT_STATE_UNREADABLE: ${message}`);
|
|
127011
|
+
this.name = "StateUnreadableError";
|
|
127012
|
+
}
|
|
127013
|
+
};
|
|
126431
127014
|
function readResourcesState() {
|
|
127015
|
+
let raw;
|
|
126432
127016
|
try {
|
|
126433
|
-
|
|
127017
|
+
raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
|
|
126434
127018
|
} catch {
|
|
126435
127019
|
return null;
|
|
126436
127020
|
}
|
|
127021
|
+
let parsed;
|
|
127022
|
+
try {
|
|
127023
|
+
parsed = load(raw);
|
|
127024
|
+
} catch (error) {
|
|
127025
|
+
throw new StateUnreadableError(
|
|
127026
|
+
`.postman/resources.yaml exists but is not parseable YAML (${error instanceof Error ? error.message : String(error)}). Fix or delete the file; refusing to treat tracked state as absent.`
|
|
127027
|
+
);
|
|
127028
|
+
}
|
|
127029
|
+
if (parsed === null || parsed === void 0) {
|
|
127030
|
+
return null;
|
|
127031
|
+
}
|
|
127032
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
127033
|
+
throw new StateUnreadableError(
|
|
127034
|
+
".postman/resources.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to treat tracked state as absent."
|
|
127035
|
+
);
|
|
127036
|
+
}
|
|
127037
|
+
const state = parsed;
|
|
127038
|
+
if (state.version !== void 0 && !SUPPORTED_STATE_VERSIONS.has(Number(state.version))) {
|
|
127039
|
+
throw new StateUnreadableError(
|
|
127040
|
+
`.postman/resources.yaml declares unsupported state version ${String(state.version)} (supported: 1, ${RESOURCES_STATE_VERSION}). Upgrade the action or fix the file.`
|
|
127041
|
+
);
|
|
127042
|
+
}
|
|
127043
|
+
if (state.canonical && !state.cloudResources) {
|
|
127044
|
+
state.cloudResources = { ...state.canonical };
|
|
127045
|
+
}
|
|
127046
|
+
return state;
|
|
126437
127047
|
}
|
|
126438
127048
|
function findCloudResourceId(map, matcher) {
|
|
126439
127049
|
if (!map) {
|
|
@@ -126468,7 +127078,7 @@ function isOpenApiSpecFile(filePath) {
|
|
|
126468
127078
|
return false;
|
|
126469
127079
|
}
|
|
126470
127080
|
try {
|
|
126471
|
-
const raw = (0,
|
|
127081
|
+
const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
|
|
126472
127082
|
const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
|
|
126473
127083
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
126474
127084
|
return false;
|
|
@@ -126497,7 +127107,7 @@ function scanLocalSpecReferences(baseDir = ".") {
|
|
|
126497
127107
|
const found = /* @__PURE__ */ new Set();
|
|
126498
127108
|
const refs = [];
|
|
126499
127109
|
const visit2 = (currentDir) => {
|
|
126500
|
-
for (const entry of (0,
|
|
127110
|
+
for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
|
|
126501
127111
|
if (ignoredDirs.has(entry.name)) {
|
|
126502
127112
|
continue;
|
|
126503
127113
|
}
|
|
@@ -126527,7 +127137,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
|
|
|
126527
127137
|
const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
|
|
126528
127138
|
if (normalizedExplicitPath) {
|
|
126529
127139
|
const explicitFullPath = path3.resolve(normalizedExplicitPath);
|
|
126530
|
-
if ((0,
|
|
127140
|
+
if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
|
|
126531
127141
|
return {
|
|
126532
127142
|
repoRelativePath: normalizedExplicitPath,
|
|
126533
127143
|
configRelativePath: normalizeToPosix(path3.join("..", normalizedExplicitPath))
|
|
@@ -126554,7 +127164,11 @@ function createOutputs(inputs) {
|
|
|
126554
127164
|
"mock-url": "",
|
|
126555
127165
|
"monitor-id": "",
|
|
126556
127166
|
"repo-sync-summary-json": "{}",
|
|
126557
|
-
"commit-sha": ""
|
|
127167
|
+
"commit-sha": "",
|
|
127168
|
+
"sync-status": "",
|
|
127169
|
+
"branch-decision": "",
|
|
127170
|
+
"spec-version-tag": "",
|
|
127171
|
+
"spec-version-url": ""
|
|
126558
127172
|
};
|
|
126559
127173
|
}
|
|
126560
127174
|
function buildGhCliEnv(env, token) {
|
|
@@ -126579,7 +127193,7 @@ function buildGhCliEnv(env, token) {
|
|
|
126579
127193
|
}
|
|
126580
127194
|
return filtered;
|
|
126581
127195
|
}
|
|
126582
|
-
async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
127196
|
+
async function upsertEnvironments(inputs, dependencies, resourcesState, assetMarker) {
|
|
126583
127197
|
const envUids = {
|
|
126584
127198
|
...getEnvironmentUidsFromResources(resourcesState),
|
|
126585
127199
|
...inputs.environmentUids
|
|
@@ -126589,7 +127203,6 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
126589
127203
|
}
|
|
126590
127204
|
for (const envName of inputs.environments) {
|
|
126591
127205
|
const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
|
|
126592
|
-
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
126593
127206
|
const displayName = `${inputs.projectName} - ${envName}`;
|
|
126594
127207
|
let existingUid = String(envUids[envName] || "").trim();
|
|
126595
127208
|
if (!existingUid) {
|
|
@@ -126605,10 +127218,27 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
126605
127218
|
}
|
|
126606
127219
|
}
|
|
126607
127220
|
if (existingUid) {
|
|
126608
|
-
|
|
127221
|
+
let marker = assetMarker;
|
|
127222
|
+
if (marker) {
|
|
127223
|
+
try {
|
|
127224
|
+
const existing = await dependencies.postman.getEnvironment(existingUid);
|
|
127225
|
+
const values3 = existing.data?.values ?? existing.values ?? [];
|
|
127226
|
+
const prior = values3.find((value) => value.key === "x-pm-onboarding")?.value;
|
|
127227
|
+
const priorMarker = parseAssetMarker(prior ? `x-pm-onboarding: ${prior}` : void 0);
|
|
127228
|
+
if (priorMarker?.repo === marker.repo && priorMarker.rawBranch === marker.rawBranch) {
|
|
127229
|
+
marker = { ...marker, createdAt: priorMarker.createdAt };
|
|
127230
|
+
}
|
|
127231
|
+
} catch {
|
|
127232
|
+
}
|
|
127233
|
+
}
|
|
127234
|
+
const values2 = buildEnvironmentValues(envName, runtimeUrl);
|
|
127235
|
+
if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
|
|
127236
|
+
await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
|
|
126609
127237
|
envUids[envName] = existingUid;
|
|
126610
127238
|
continue;
|
|
126611
127239
|
}
|
|
127240
|
+
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
127241
|
+
if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
|
|
126612
127242
|
envUids[envName] = await dependencies.postman.createEnvironment(
|
|
126613
127243
|
inputs.workspaceId,
|
|
126614
127244
|
displayName,
|
|
@@ -126618,7 +127248,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
126618
127248
|
return envUids;
|
|
126619
127249
|
}
|
|
126620
127250
|
function ensureDir(path5) {
|
|
126621
|
-
(0,
|
|
127251
|
+
(0, import_node_fs4.mkdirSync)(path5, { recursive: true });
|
|
126622
127252
|
}
|
|
126623
127253
|
function getCollectionDirectoryName(kind, projectName) {
|
|
126624
127254
|
if (kind === "Baseline") {
|
|
@@ -126655,7 +127285,7 @@ function stripVolatileFields(obj) {
|
|
|
126655
127285
|
}
|
|
126656
127286
|
function writeJsonFile(path5, content, normalize3 = false) {
|
|
126657
127287
|
const data = normalize3 ? stripVolatileFields(content) : content;
|
|
126658
|
-
(0,
|
|
127288
|
+
(0, import_node_fs4.writeFileSync)(path5, JSON.stringify(data, null, 2));
|
|
126659
127289
|
}
|
|
126660
127290
|
function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
|
|
126661
127291
|
if (specSyncMode !== "version") {
|
|
@@ -126679,31 +127309,30 @@ function resolveDurableWorkspaceId(options) {
|
|
|
126679
127309
|
}
|
|
126680
127310
|
return prior === candidate ? prior : void 0;
|
|
126681
127311
|
}
|
|
126682
|
-
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs) {
|
|
126683
|
-
const manifest = {};
|
|
127312
|
+
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs, priorState) {
|
|
127313
|
+
const manifest = { ...priorState ?? {} };
|
|
127314
|
+
delete manifest.version;
|
|
127315
|
+
delete manifest.workspace;
|
|
127316
|
+
delete manifest.localResources;
|
|
127317
|
+
delete manifest.cloudResources;
|
|
127318
|
+
delete manifest.canonical;
|
|
127319
|
+
manifest.version = RESOURCES_STATE_VERSION;
|
|
126684
127320
|
if (workspaceId) {
|
|
126685
127321
|
manifest.workspace = { id: workspaceId };
|
|
126686
127322
|
}
|
|
126687
|
-
const localResources = {};
|
|
126688
127323
|
const cloudResources = {};
|
|
126689
127324
|
const collectionKeys = Object.keys(collectionMap);
|
|
126690
127325
|
if (collectionKeys.length > 0) {
|
|
126691
|
-
localResources.collections = collectionKeys;
|
|
126692
127326
|
cloudResources.collections = collectionMap;
|
|
126693
127327
|
}
|
|
126694
127328
|
const envEntries = Object.entries(envMap);
|
|
126695
127329
|
if (envEntries.length > 0) {
|
|
126696
|
-
localResources.environments = envEntries.map(
|
|
126697
|
-
([envName]) => `../${artifactDir}/environments/${envName}.postman_environment.json`
|
|
126698
|
-
);
|
|
126699
127330
|
cloudResources.environments = {};
|
|
126700
127331
|
for (const [envName, envUid] of envEntries) {
|
|
126701
127332
|
cloudResources.environments[`../${artifactDir}/environments/${envName}.postman_environment.json`] = envUid;
|
|
126702
127333
|
}
|
|
126703
127334
|
}
|
|
126704
|
-
|
|
126705
|
-
localResources.specs = localSpecRefs;
|
|
126706
|
-
}
|
|
127335
|
+
void localSpecRefs;
|
|
126707
127336
|
const specs = { ...existingSpecs || {} };
|
|
126708
127337
|
if (mappedSpecRef && specId) {
|
|
126709
127338
|
specs[mappedSpecRef] = specId;
|
|
@@ -126711,11 +127340,8 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
|
|
|
126711
127340
|
if (Object.keys(specs).length > 0) {
|
|
126712
127341
|
cloudResources.specs = specs;
|
|
126713
127342
|
}
|
|
126714
|
-
if (Object.keys(localResources).length > 0) {
|
|
126715
|
-
manifest.localResources = localResources;
|
|
126716
|
-
}
|
|
126717
127343
|
if (Object.keys(cloudResources).length > 0) {
|
|
126718
|
-
manifest.
|
|
127344
|
+
manifest.canonical = cloudResources;
|
|
126719
127345
|
}
|
|
126720
127346
|
return dump(manifest, {
|
|
126721
127347
|
lineWidth: -1,
|
|
@@ -126756,21 +127382,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
|
|
|
126756
127382
|
if (!rawPath || hasControlCharacter2(originalPath) || path3.isAbsolute(rawPath) || path3.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
|
|
126757
127383
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
126758
127384
|
}
|
|
126759
|
-
const base = (0,
|
|
127385
|
+
const base = (0, import_node_fs4.realpathSync)(process.cwd());
|
|
126760
127386
|
const resolved = path3.resolve(base, rawPath);
|
|
126761
127387
|
const relative3 = path3.relative(base, resolved);
|
|
126762
127388
|
if (relative3.startsWith("..") || path3.isAbsolute(relative3)) {
|
|
126763
127389
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
126764
127390
|
}
|
|
126765
127391
|
let existingPath = resolved;
|
|
126766
|
-
while (!(0,
|
|
127392
|
+
while (!(0, import_node_fs4.existsSync)(existingPath)) {
|
|
126767
127393
|
const parent = path3.dirname(existingPath);
|
|
126768
127394
|
if (parent === existingPath) {
|
|
126769
127395
|
break;
|
|
126770
127396
|
}
|
|
126771
127397
|
existingPath = parent;
|
|
126772
127398
|
}
|
|
126773
|
-
const realExistingPath = (0,
|
|
127399
|
+
const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
|
|
126774
127400
|
const realRelative = path3.relative(base, realExistingPath);
|
|
126775
127401
|
if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
|
|
126776
127402
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
@@ -126798,8 +127424,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
126798
127424
|
ensureDir(specsDir);
|
|
126799
127425
|
ensureDir(".postman");
|
|
126800
127426
|
const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
|
|
126801
|
-
if (!(0,
|
|
126802
|
-
(0,
|
|
127427
|
+
if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
|
|
127428
|
+
(0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
|
|
126803
127429
|
}
|
|
126804
127430
|
if (inputs.generateCiWorkflow) {
|
|
126805
127431
|
const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
|
|
@@ -126846,7 +127472,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
126846
127472
|
workspaceLinkEnabled: inputs.workspaceLinkEnabled,
|
|
126847
127473
|
workspaceLinkStatus: options.workspaceLinkStatus
|
|
126848
127474
|
});
|
|
126849
|
-
(0,
|
|
127475
|
+
(0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
126850
127476
|
durableWorkspaceId,
|
|
126851
127477
|
manifestCollections,
|
|
126852
127478
|
envUids,
|
|
@@ -126854,10 +127480,11 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
126854
127480
|
discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
126855
127481
|
mappedSpecCloudKey,
|
|
126856
127482
|
inputs.specId || void 0,
|
|
126857
|
-
options.existingSpecs
|
|
127483
|
+
options.existingSpecs,
|
|
127484
|
+
options.priorState
|
|
126858
127485
|
));
|
|
126859
127486
|
if (mappedSpec && Object.keys(manifestCollections).length > 0) {
|
|
126860
|
-
(0,
|
|
127487
|
+
(0, import_node_fs4.writeFileSync)(
|
|
126861
127488
|
".postman/workflows.yaml",
|
|
126862
127489
|
buildSpecCollectionWorkflowManifest(
|
|
126863
127490
|
mappedSpec.configRelativePath,
|
|
@@ -126902,19 +127529,29 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
|
|
|
126902
127529
|
const dir = parts.slice(0, -1).join("/");
|
|
126903
127530
|
ensureDir(dir);
|
|
126904
127531
|
}
|
|
126905
|
-
(0,
|
|
127532
|
+
(0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
|
|
127533
|
+
if (inputs.provider === "github" || inputs.provider === "unknown") {
|
|
127534
|
+
const gcPath = ".github/workflows/postman-preview-gc.yml";
|
|
127535
|
+
if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
|
|
127536
|
+
ensureDir(".github/workflows");
|
|
127537
|
+
(0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
|
|
127538
|
+
}
|
|
127539
|
+
}
|
|
126906
127540
|
}
|
|
126907
127541
|
if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
|
|
126908
127542
|
return { commitSha: "", resolvedCurrentRef: "", pushed: false };
|
|
126909
127543
|
}
|
|
126910
127544
|
const provisionPath = ".github/workflows/provision.yml";
|
|
126911
|
-
const provisionExists = inputs.provider === "github" && (0,
|
|
127545
|
+
const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
|
|
127546
|
+
const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
|
|
127547
|
+
const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
|
|
126912
127548
|
const stagePaths = [
|
|
126913
127549
|
inputs.artifactDir,
|
|
126914
127550
|
".postman",
|
|
126915
127551
|
inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
|
|
127552
|
+
gcExists ? gcWorkflowPath : null,
|
|
126916
127553
|
provisionExists ? provisionPath : null
|
|
126917
|
-
].filter((entry) => typeof entry === "string" && ((0,
|
|
127554
|
+
].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
|
|
126918
127555
|
if (stagePaths.length === 0) {
|
|
126919
127556
|
dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
|
|
126920
127557
|
return {
|
|
@@ -126962,6 +127599,31 @@ async function runRepoSync(inputs, dependencies) {
|
|
|
126962
127599
|
}
|
|
126963
127600
|
}
|
|
126964
127601
|
async function runRepoSyncInner(inputs, dependencies) {
|
|
127602
|
+
const branchDecision = decideBranchTier(inputs);
|
|
127603
|
+
assertBranchAssetIds(inputs, branchDecision);
|
|
127604
|
+
const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
|
|
127605
|
+
if (!isCanonicalWriter) {
|
|
127606
|
+
if (branchDecision.tier === "preview" && branchDecision.identity.headBranch) {
|
|
127607
|
+
inputs = {
|
|
127608
|
+
...inputs,
|
|
127609
|
+
projectName: previewAssetName(inputs.projectName, branchDecision.identity.headBranch),
|
|
127610
|
+
workspaceLinkEnabled: false,
|
|
127611
|
+
environmentSyncEnabled: false,
|
|
127612
|
+
repoWriteMode: "none"
|
|
127613
|
+
};
|
|
127614
|
+
} else if (branchDecision.tier === "channel" && branchDecision.channel) {
|
|
127615
|
+
inputs = {
|
|
127616
|
+
...inputs,
|
|
127617
|
+
projectName: channelAssetName(inputs.projectName, branchDecision.channel.code),
|
|
127618
|
+
workspaceLinkEnabled: false,
|
|
127619
|
+
environmentSyncEnabled: false,
|
|
127620
|
+
repoWriteMode: "none"
|
|
127621
|
+
};
|
|
127622
|
+
}
|
|
127623
|
+
dependencies.core.info(
|
|
127624
|
+
`branch-aware sync: ${branchDecision.tier} run \u2014 branch-scoped asset set "${inputs.projectName}", no workspace repo-link mutation, no state write-back`
|
|
127625
|
+
);
|
|
127626
|
+
}
|
|
126965
127627
|
const outputs = createOutputs(inputs);
|
|
126966
127628
|
const versionRequested = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
|
|
126967
127629
|
const releaseLabel = deriveReleaseLabel(inputs);
|
|
@@ -126969,7 +127631,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
126969
127631
|
throw new Error("release-label is required when collection-sync-mode or spec-sync-mode is version");
|
|
126970
127632
|
}
|
|
126971
127633
|
const assetProjectName = createAssetProjectName(inputs, releaseLabel);
|
|
126972
|
-
const
|
|
127634
|
+
const trackedState = readResourcesState();
|
|
127635
|
+
const resourcesState = isCanonicalWriter ? trackedState : trackedState?.workspace ? { workspace: trackedState.workspace } : null;
|
|
126973
127636
|
if (resourcesState) {
|
|
126974
127637
|
if (!inputs.workspaceId && resourcesState.workspace?.id) {
|
|
126975
127638
|
inputs.workspaceId = resourcesState.workspace.id;
|
|
@@ -126995,7 +127658,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
126995
127658
|
}
|
|
126996
127659
|
}
|
|
126997
127660
|
}
|
|
126998
|
-
const
|
|
127661
|
+
const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
|
|
127662
|
+
const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
|
|
126999
127663
|
outputs["environment-uids-json"] = JSON.stringify(envUids);
|
|
127000
127664
|
if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
|
|
127001
127665
|
const associations = Object.entries(envUids).map(([envName, envUid]) => ({
|
|
@@ -127015,6 +127679,19 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
127015
127679
|
`System environment association failed: ${error instanceof Error ? error.message : String(error)}`
|
|
127016
127680
|
);
|
|
127017
127681
|
}
|
|
127682
|
+
} else if (Object.keys(envUids).length > 0) {
|
|
127683
|
+
const configuredKeys = Object.keys(inputs.systemEnvMap).filter(
|
|
127684
|
+
(key) => Boolean(String(inputs.systemEnvMap[key] ?? "").trim())
|
|
127685
|
+
);
|
|
127686
|
+
if (configuredKeys.length === 0) {
|
|
127687
|
+
dependencies.core.warning(
|
|
127688
|
+
'system-env-map-json is empty while environment-sync-enabled is true. Workspace\u2194git linking still registers the service in API Catalog, but Catalog system-environment filters (Prod/Stage/Dev) hide services until Postman environments are associated. Pass system-env-map-json like {"prod":"<system-env-uuid>"} or clear the Catalog system-environment filter to see the service.'
|
|
127689
|
+
);
|
|
127690
|
+
} else {
|
|
127691
|
+
dependencies.core.warning(
|
|
127692
|
+
`system-env-map-json keys (${configuredKeys.join(", ")}) did not match any synced environment (${Object.keys(envUids).join(", ")}). No system-environment associations were made; Catalog system-environment filters may hide this service.`
|
|
127693
|
+
);
|
|
127694
|
+
}
|
|
127018
127695
|
}
|
|
127019
127696
|
}
|
|
127020
127697
|
if (inputs.workspaceId && inputs.baselineCollectionId && Object.keys(envUids).length > 0) {
|
|
@@ -127038,11 +127715,23 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
127038
127715
|
}
|
|
127039
127716
|
}
|
|
127040
127717
|
if (!resolvedMockUrl) {
|
|
127041
|
-
const mock = await
|
|
127042
|
-
|
|
127043
|
-
|
|
127044
|
-
|
|
127045
|
-
|
|
127718
|
+
const mock = await retry(
|
|
127719
|
+
() => dependencies.postman.createMock(
|
|
127720
|
+
inputs.workspaceId,
|
|
127721
|
+
mockName,
|
|
127722
|
+
inputs.baselineCollectionId,
|
|
127723
|
+
mockEnvUid
|
|
127724
|
+
),
|
|
127725
|
+
{
|
|
127726
|
+
maxAttempts: 3,
|
|
127727
|
+
delayMs: 2e3,
|
|
127728
|
+
backoffMultiplier: 2,
|
|
127729
|
+
onRetry: ({ attempt, error }) => {
|
|
127730
|
+
dependencies.core.warning(
|
|
127731
|
+
`Mock create attempt ${attempt} failed (${error instanceof Error ? error.message : String(error)}); retrying`
|
|
127732
|
+
);
|
|
127733
|
+
}
|
|
127734
|
+
}
|
|
127046
127735
|
);
|
|
127047
127736
|
resolvedMockUrl = mock.url;
|
|
127048
127737
|
dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
|
|
@@ -127106,18 +127795,24 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
127106
127795
|
inputs.repoUrl
|
|
127107
127796
|
);
|
|
127108
127797
|
outputs["workspace-link-status"] = "success";
|
|
127798
|
+
dependencies.core.info(
|
|
127799
|
+
`workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl}`
|
|
127800
|
+
);
|
|
127109
127801
|
} catch (error) {
|
|
127110
127802
|
outputs["workspace-link-status"] = "failed";
|
|
127111
|
-
|
|
127112
|
-
|
|
127113
|
-
|
|
127803
|
+
const message = `Workspace link failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
127804
|
+
if (branchDecision.tier === "canonical") {
|
|
127805
|
+
throw new Error(message, { cause: error });
|
|
127806
|
+
}
|
|
127807
|
+
dependencies.core.warning(message);
|
|
127114
127808
|
}
|
|
127115
127809
|
}
|
|
127116
127810
|
await exportArtifacts(inputs, dependencies, envUids, assetProjectName, {
|
|
127117
127811
|
workspaceLinkStatus: outputs["workspace-link-status"],
|
|
127118
127812
|
priorWorkspaceId: resourcesState?.workspace?.id,
|
|
127119
127813
|
existingSpecs: resourcesState?.cloudResources?.specs,
|
|
127120
|
-
releaseLabel
|
|
127814
|
+
releaseLabel,
|
|
127815
|
+
priorState: resourcesState
|
|
127121
127816
|
});
|
|
127122
127817
|
const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
|
|
127123
127818
|
outputs["commit-sha"] = commit.commitSha;
|
|
@@ -127125,6 +127820,38 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
127125
127820
|
outputs["resolved-current-ref"] = commit.resolvedCurrentRef;
|
|
127126
127821
|
}
|
|
127127
127822
|
outputs["repo-sync-summary-json"] = createRepoSummary(outputs, envUids, commit.pushed);
|
|
127823
|
+
if (branchDecision.tier === "canonical" && inputs.specId && inputs.specContentChanged !== false && dependencies.postman.tagSpecVersion) {
|
|
127824
|
+
const shortSha = (branchDecision.identity.headSha ?? "").slice(0, 7);
|
|
127825
|
+
const tagName = inputs.releaseLabel ? `${inputs.releaseLabel}${shortSha ? ` (${shortSha})` : ""}` : shortSha || `sync-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
127826
|
+
try {
|
|
127827
|
+
const tag = await dependencies.postman.tagSpecVersion(inputs.specId, tagName);
|
|
127828
|
+
outputs["spec-version-tag"] = tag.name || tagName;
|
|
127829
|
+
if (tag.id) {
|
|
127830
|
+
outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(tag.id)}&versionLabel=${encodeURIComponent(tag.name || tagName)}`;
|
|
127831
|
+
}
|
|
127832
|
+
dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
|
|
127833
|
+
} catch (error) {
|
|
127834
|
+
const status = error.status;
|
|
127835
|
+
if (status === 409) {
|
|
127836
|
+
const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
|
|
127837
|
+
const latest = tags[0];
|
|
127838
|
+
if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
|
|
127839
|
+
outputs["spec-version-tag"] = latest.name;
|
|
127840
|
+
outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
|
|
127841
|
+
dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
|
|
127842
|
+
} else {
|
|
127843
|
+
dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
|
|
127844
|
+
}
|
|
127845
|
+
} else {
|
|
127846
|
+
dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error instanceof Error ? error.message : String(error)}`);
|
|
127847
|
+
}
|
|
127848
|
+
}
|
|
127849
|
+
}
|
|
127850
|
+
if (inputs.branchStrategy !== "legacy" || process.env[BRANCH_DECISION_ENV]) {
|
|
127851
|
+
const decision = decideBranchTier(inputs);
|
|
127852
|
+
outputs["sync-status"] = "synced";
|
|
127853
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
127854
|
+
}
|
|
127128
127855
|
for (const [name, value] of Object.entries(outputs)) {
|
|
127129
127856
|
dependencies.core.setOutput(name, value);
|
|
127130
127857
|
}
|
|
@@ -127314,7 +128041,19 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
127314
128041
|
listMonitors: gatewayAssets.listMonitors.bind(gatewayAssets),
|
|
127315
128042
|
monitorExists: gatewayAssets.monitorExists.bind(gatewayAssets),
|
|
127316
128043
|
findMonitorByCollection: gatewayAssets.findMonitorByCollection.bind(gatewayAssets),
|
|
127317
|
-
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets)
|
|
128044
|
+
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets),
|
|
128045
|
+
listEnvironments: gatewayAssets.listEnvironments.bind(gatewayAssets),
|
|
128046
|
+
// GC path (preview/channel retention machine — lib/repo/preview-gc.ts).
|
|
128047
|
+
deleteEnvironment: gatewayAssets.deleteEnvironment.bind(gatewayAssets),
|
|
128048
|
+
deleteMock: gatewayAssets.deleteMock.bind(gatewayAssets),
|
|
128049
|
+
deleteMonitor: gatewayAssets.deleteMonitor.bind(gatewayAssets),
|
|
128050
|
+
deleteCollection: gatewayAssets.deleteCollection.bind(gatewayAssets),
|
|
128051
|
+
listSpecifications: gatewayAssets.listSpecifications.bind(gatewayAssets),
|
|
128052
|
+
getSpecContent: gatewayAssets.getSpecContent.bind(gatewayAssets),
|
|
128053
|
+
listSpecCollections: gatewayAssets.listSpecCollections.bind(gatewayAssets),
|
|
128054
|
+
deleteSpec: gatewayAssets.deleteSpec.bind(gatewayAssets),
|
|
128055
|
+
tagSpecVersion: gatewayAssets.tagSpecVersion.bind(gatewayAssets),
|
|
128056
|
+
listSpecVersionTags: gatewayAssets.listSpecVersionTags.bind(gatewayAssets)
|
|
127318
128057
|
};
|
|
127319
128058
|
const repoMutation = repository && (inputs.repoWriteMode === "commit-only" || inputs.repoWriteMode === "commit-and-push") ? new RepoMutationService({
|
|
127320
128059
|
provider: inputs.provider,
|
|
@@ -127351,6 +128090,400 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
127351
128090
|
repoMutation
|
|
127352
128091
|
};
|
|
127353
128092
|
}
|
|
128093
|
+
function decideBranchTier(inputs, env = process.env) {
|
|
128094
|
+
return resolveEffectiveBranchDecision(
|
|
128095
|
+
{
|
|
128096
|
+
strategy: inputs.branchStrategy,
|
|
128097
|
+
identity: resolveBranchIdentity(env, { defaultBranch: inputs.canonicalBranch }),
|
|
128098
|
+
canonicalBranch: inputs.canonicalBranch,
|
|
128099
|
+
channels: parseChannelRules(inputs.channels)
|
|
128100
|
+
},
|
|
128101
|
+
env
|
|
128102
|
+
);
|
|
128103
|
+
}
|
|
128104
|
+
function runGatedSkip(inputs, decision, actionCore) {
|
|
128105
|
+
actionCore.info(
|
|
128106
|
+
`branch-aware sync: gated run (${decision.reason}) \u2014 repo-sync skipped, zero workspace writes`
|
|
128107
|
+
);
|
|
128108
|
+
const outputs = createOutputs(inputs);
|
|
128109
|
+
outputs["sync-status"] = "skipped-branch-gate";
|
|
128110
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
128111
|
+
outputs["repo-sync-summary-json"] = JSON.stringify({
|
|
128112
|
+
status: "skipped-branch-gate",
|
|
128113
|
+
reason: decision.reason
|
|
128114
|
+
});
|
|
128115
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
128116
|
+
actionCore.setOutput(name, value);
|
|
128117
|
+
}
|
|
128118
|
+
return outputs;
|
|
128119
|
+
}
|
|
128120
|
+
|
|
128121
|
+
// src/lib/repo/preview-gc.ts
|
|
128122
|
+
function looksGenerated(name) {
|
|
128123
|
+
return / @[A-Za-z0-9._-]+/.test(name) || /^\[[A-Z][A-Z0-9]*\] /.test(name);
|
|
128124
|
+
}
|
|
128125
|
+
function resolveMarker(candidate) {
|
|
128126
|
+
return candidate.marker ?? parseAssetMarker(candidate.description);
|
|
128127
|
+
}
|
|
128128
|
+
function stampChannelRetirement(marker, reason, now, previewTtlDays) {
|
|
128129
|
+
const deleteAfter = new Date(now.getTime() + previewTtlDays * 24 * 60 * 60 * 1e3);
|
|
128130
|
+
return {
|
|
128131
|
+
...marker,
|
|
128132
|
+
retirementDetectedAt: now.toISOString(),
|
|
128133
|
+
retirementReason: reason,
|
|
128134
|
+
deleteAfter: deleteAfter.toISOString()
|
|
128135
|
+
};
|
|
128136
|
+
}
|
|
128137
|
+
function clearChannelRetirement(marker) {
|
|
128138
|
+
const next = { ...marker };
|
|
128139
|
+
delete next.retirementDetectedAt;
|
|
128140
|
+
delete next.retirementReason;
|
|
128141
|
+
delete next.deleteAfter;
|
|
128142
|
+
return next;
|
|
128143
|
+
}
|
|
128144
|
+
function decideRetention(candidate, context) {
|
|
128145
|
+
const marker = resolveMarker(candidate);
|
|
128146
|
+
if (!marker) {
|
|
128147
|
+
if (looksGenerated(candidate.name)) {
|
|
128148
|
+
return { action: "orphan-audit", reason: "generated-looking name with missing or invalid marker" };
|
|
128149
|
+
}
|
|
128150
|
+
return { action: "stranger", reason: "no marker; not our asset" };
|
|
128151
|
+
}
|
|
128152
|
+
if (marker.repo !== context.repo) {
|
|
128153
|
+
return { action: "stranger", reason: `marker repo ${marker.repo} does not match ${context.repo}` };
|
|
128154
|
+
}
|
|
128155
|
+
if (context.triggerGeneration && marker.createdAt) {
|
|
128156
|
+
const created = new Date(marker.createdAt);
|
|
128157
|
+
if (!Number.isNaN(created.getTime()) && created.getTime() > context.triggerGeneration.getTime()) {
|
|
128158
|
+
return { action: "retain", reason: "marker generation is newer than the GC trigger (name reuse)" };
|
|
128159
|
+
}
|
|
128160
|
+
}
|
|
128161
|
+
if (marker.role === "channel") {
|
|
128162
|
+
if (marker.retirementReason && marker.deleteAfter) {
|
|
128163
|
+
const existence3 = context.branchExists(marker.rawBranch);
|
|
128164
|
+
const mapped = context.channelMapped?.(marker.rawBranch);
|
|
128165
|
+
const restored = marker.retirementReason === "branch-deleted" ? existence3 === "exists" && mapped !== false : existence3 === "exists" && mapped === true;
|
|
128166
|
+
if (restored) {
|
|
128167
|
+
return { action: "restore", reason: "retired channel branch and mapping are active again", branchProbe: "exists" };
|
|
128168
|
+
}
|
|
128169
|
+
const deleteAfter = new Date(marker.deleteAfter);
|
|
128170
|
+
if (!Number.isNaN(deleteAfter.getTime()) && context.now.getTime() >= deleteAfter.getTime()) {
|
|
128171
|
+
return { action: "delete", reason: `retired channel set past deleteAfter (${marker.retirementReason})` };
|
|
128172
|
+
}
|
|
128173
|
+
return { action: "retain", reason: "retired channel set inside deleteAfter window" };
|
|
128174
|
+
}
|
|
128175
|
+
const existence2 = context.branchExists(marker.rawBranch);
|
|
128176
|
+
if (existence2 === "deleted") {
|
|
128177
|
+
return { action: "retire", reason: "channel branch deleted; start retirement window", branchProbe: "deleted", retirementReason: "branch-deleted" };
|
|
128178
|
+
}
|
|
128179
|
+
if (context.channelMapped?.(marker.rawBranch) === false) {
|
|
128180
|
+
return { action: "retire", reason: "channel mapping removed; start retirement window", branchProbe: existence2, retirementReason: "mapping-removed" };
|
|
128181
|
+
}
|
|
128182
|
+
return { action: "retain", reason: "active channel set (channels are never TTL-swept)", branchProbe: existence2 };
|
|
128183
|
+
}
|
|
128184
|
+
if (context.onlyBranch !== void 0) {
|
|
128185
|
+
if (marker.rawBranch !== context.onlyBranch && marker.sanitizedBranch !== context.onlyBranch) {
|
|
128186
|
+
return { action: "retain", reason: "outside manual --branch scope" };
|
|
128187
|
+
}
|
|
128188
|
+
return { action: "delete", reason: `manual gc --branch ${context.onlyBranch}` };
|
|
128189
|
+
}
|
|
128190
|
+
if (context.allPreviews) {
|
|
128191
|
+
return { action: "delete", reason: "manual gc --all-previews" };
|
|
128192
|
+
}
|
|
128193
|
+
const existence = context.branchExists(marker.rawBranch);
|
|
128194
|
+
if (existence === "exists") {
|
|
128195
|
+
return { action: "retain", reason: "branch still exists", branchProbe: "exists" };
|
|
128196
|
+
}
|
|
128197
|
+
if (existence === "deleted") {
|
|
128198
|
+
return { action: "delete", reason: "branch deleted", branchProbe: "deleted" };
|
|
128199
|
+
}
|
|
128200
|
+
if (marker.expiresAt) {
|
|
128201
|
+
const expires = new Date(marker.expiresAt);
|
|
128202
|
+
if (!Number.isNaN(expires.getTime()) && context.now.getTime() >= expires.getTime()) {
|
|
128203
|
+
return { action: "delete", reason: "sliding TTL expired", branchProbe: "skipped" };
|
|
128204
|
+
}
|
|
128205
|
+
}
|
|
128206
|
+
return { action: "retain", reason: "branch existence unknown and TTL not expired", branchProbe: "skipped" };
|
|
128207
|
+
}
|
|
128208
|
+
async function runPreviewGc(options) {
|
|
128209
|
+
const { context, candidates, deleters, dryRun } = options;
|
|
128210
|
+
const log = options.log ?? (() => void 0);
|
|
128211
|
+
const summary2 = {
|
|
128212
|
+
repo: context.repo,
|
|
128213
|
+
scannedAt: context.now.toISOString(),
|
|
128214
|
+
degraded: Boolean(options.degraded),
|
|
128215
|
+
counts: { delete: 0, retire: 0, restore: 0, retain: 0, stranger: 0, "orphan-audit": 0, errors: 0 },
|
|
128216
|
+
entries: []
|
|
128217
|
+
};
|
|
128218
|
+
for (const candidate of candidates) {
|
|
128219
|
+
const decision = decideRetention(candidate, context);
|
|
128220
|
+
const entry = {
|
|
128221
|
+
kind: candidate.kind,
|
|
128222
|
+
uid: candidate.uid,
|
|
128223
|
+
name: candidate.name,
|
|
128224
|
+
action: decision.action,
|
|
128225
|
+
reason: decision.reason
|
|
128226
|
+
};
|
|
128227
|
+
summary2.counts[decision.action] += 1;
|
|
128228
|
+
if (decision.action === "retire" && !dryRun && candidate.kind === "environment") {
|
|
128229
|
+
const retire = options.retirers?.environment;
|
|
128230
|
+
if (!retire) {
|
|
128231
|
+
entry.error = "no channel retirement writer wired for environment";
|
|
128232
|
+
summary2.counts.errors += 1;
|
|
128233
|
+
} else {
|
|
128234
|
+
try {
|
|
128235
|
+
await retire(candidate, decision.retirementReason ?? "branch-deleted");
|
|
128236
|
+
log(`gc: retired channel ${candidate.name} (${candidate.uid}) \u2014 ${decision.reason}`);
|
|
128237
|
+
} catch (error) {
|
|
128238
|
+
entry.error = error instanceof Error ? error.message : String(error);
|
|
128239
|
+
summary2.counts.errors += 1;
|
|
128240
|
+
}
|
|
128241
|
+
}
|
|
128242
|
+
} else if (decision.action === "restore" && !dryRun && candidate.kind === "environment") {
|
|
128243
|
+
const restore = options.retirers?.restoreEnvironment;
|
|
128244
|
+
if (!restore) {
|
|
128245
|
+
entry.error = "no channel retirement restore writer wired for environment";
|
|
128246
|
+
summary2.counts.errors += 1;
|
|
128247
|
+
} else {
|
|
128248
|
+
try {
|
|
128249
|
+
await restore(candidate);
|
|
128250
|
+
log(`gc: restored channel ${candidate.name} (${candidate.uid})`);
|
|
128251
|
+
} catch (error) {
|
|
128252
|
+
entry.error = error instanceof Error ? error.message : String(error);
|
|
128253
|
+
summary2.counts.errors += 1;
|
|
128254
|
+
}
|
|
128255
|
+
}
|
|
128256
|
+
} else if (decision.action === "delete" && !dryRun) {
|
|
128257
|
+
const remove = deleters[candidate.kind];
|
|
128258
|
+
if (!remove) {
|
|
128259
|
+
entry.error = `no deleter wired for kind ${candidate.kind}`;
|
|
128260
|
+
summary2.counts.errors += 1;
|
|
128261
|
+
} else {
|
|
128262
|
+
try {
|
|
128263
|
+
await remove(candidate.uid);
|
|
128264
|
+
entry.deleted = true;
|
|
128265
|
+
log(`gc: deleted ${candidate.kind} ${candidate.name} (${candidate.uid}) \u2014 ${decision.reason}`);
|
|
128266
|
+
} catch (error) {
|
|
128267
|
+
entry.error = error instanceof Error ? error.message : String(error);
|
|
128268
|
+
summary2.counts.errors += 1;
|
|
128269
|
+
log(`gc: FAILED deleting ${candidate.kind} ${candidate.name} (${candidate.uid}): ${entry.error}`);
|
|
128270
|
+
}
|
|
128271
|
+
}
|
|
128272
|
+
} else if (decision.action === "delete" && dryRun) {
|
|
128273
|
+
log(`gc (dry-run): would delete ${candidate.kind} ${candidate.name} (${candidate.uid}) \u2014 ${decision.reason}`);
|
|
128274
|
+
}
|
|
128275
|
+
summary2.entries.push(entry);
|
|
128276
|
+
}
|
|
128277
|
+
return summary2;
|
|
128278
|
+
}
|
|
128279
|
+
function renderGcSummary(summary2) {
|
|
128280
|
+
const lines = [];
|
|
128281
|
+
lines.push(`Preview GC summary for ${summary2.repo} at ${summary2.scannedAt}${summary2.degraded ? " (degraded: branch probes skipped)" : ""}`);
|
|
128282
|
+
lines.push(
|
|
128283
|
+
` deleted=${summary2.counts.delete} retired=${summary2.counts.retire} restored=${summary2.counts.restore} retained=${summary2.counts.retain} strangers=${summary2.counts.stranger} orphans=${summary2.counts["orphan-audit"]} errors=${summary2.counts.errors}`
|
|
128284
|
+
);
|
|
128285
|
+
for (const entry of summary2.entries) {
|
|
128286
|
+
if (entry.action === "retain" || entry.action === "stranger") continue;
|
|
128287
|
+
const status = entry.error ? `ERROR: ${entry.error}` : entry.deleted ? "deleted" : entry.action;
|
|
128288
|
+
lines.push(` [${entry.kind}] ${entry.name} (${entry.uid}): ${status} \u2014 ${entry.reason}`);
|
|
128289
|
+
}
|
|
128290
|
+
return lines.join("\n");
|
|
128291
|
+
}
|
|
128292
|
+
|
|
128293
|
+
// src/lib/repo/gc-runner.ts
|
|
128294
|
+
var GC_MARKER_ENV_KEY = "x-pm-onboarding";
|
|
128295
|
+
async function inventoryRemoteBranches(exec) {
|
|
128296
|
+
try {
|
|
128297
|
+
const result = await exec.getExecOutput("git", ["ls-remote", "--heads", "origin"], {
|
|
128298
|
+
ignoreReturnCode: true
|
|
128299
|
+
});
|
|
128300
|
+
if (result.exitCode !== 0) {
|
|
128301
|
+
return void 0;
|
|
128302
|
+
}
|
|
128303
|
+
const branches = /* @__PURE__ */ new Set();
|
|
128304
|
+
for (const line of result.stdout.split("\n")) {
|
|
128305
|
+
const match = line.match(/\trefs\/heads\/(.+)$/);
|
|
128306
|
+
if (match) branches.add(match[1].trim());
|
|
128307
|
+
}
|
|
128308
|
+
return branches;
|
|
128309
|
+
} catch {
|
|
128310
|
+
return void 0;
|
|
128311
|
+
}
|
|
128312
|
+
}
|
|
128313
|
+
function markerFromEnvironment(envelope) {
|
|
128314
|
+
const record = envelope && typeof envelope === "object" ? envelope : null;
|
|
128315
|
+
const data = record && typeof record.data === "object" && record.data !== null ? record.data : record;
|
|
128316
|
+
const values = data && Array.isArray(data.values) ? data.values : [];
|
|
128317
|
+
for (const value of values) {
|
|
128318
|
+
if (String(value?.key ?? "") === GC_MARKER_ENV_KEY) {
|
|
128319
|
+
return parseAssetMarker(`${GC_MARKER_ENV_KEY}: ${String(value?.value ?? "")}`);
|
|
128320
|
+
}
|
|
128321
|
+
}
|
|
128322
|
+
return void 0;
|
|
128323
|
+
}
|
|
128324
|
+
function environmentValues(envelope) {
|
|
128325
|
+
const record = envelope && typeof envelope === "object" ? envelope : null;
|
|
128326
|
+
const data = record && typeof record.data === "object" && record.data !== null ? record.data : record;
|
|
128327
|
+
const values = data && Array.isArray(data.values) ? data.values : [];
|
|
128328
|
+
return values.map((raw) => {
|
|
128329
|
+
const value = raw && typeof raw === "object" ? raw : {};
|
|
128330
|
+
return {
|
|
128331
|
+
key: String(value.key ?? ""),
|
|
128332
|
+
value: String(value.value ?? ""),
|
|
128333
|
+
enabled: value.enabled !== false,
|
|
128334
|
+
type: String(value.type ?? "default")
|
|
128335
|
+
};
|
|
128336
|
+
});
|
|
128337
|
+
}
|
|
128338
|
+
function markerFromSpecContent(content) {
|
|
128339
|
+
if (!content) return void 0;
|
|
128340
|
+
try {
|
|
128341
|
+
const parsed = load(content);
|
|
128342
|
+
const marker = parsed?.["x-postman-onboarding"];
|
|
128343
|
+
return marker && typeof marker === "object" ? parseAssetMarker(`${GC_MARKER_ENV_KEY}: ${JSON.stringify(marker)}`) : void 0;
|
|
128344
|
+
} catch {
|
|
128345
|
+
return void 0;
|
|
128346
|
+
}
|
|
128347
|
+
}
|
|
128348
|
+
function isGcCandidateName(name) {
|
|
128349
|
+
return / @[A-Za-z0-9._-]+/.test(name) || /^\[[A-Z][A-Z0-9]*\] /.test(name);
|
|
128350
|
+
}
|
|
128351
|
+
async function collectGcCandidates(postman, workspaceId) {
|
|
128352
|
+
const candidates = [];
|
|
128353
|
+
const environments = await postman.listEnvironments(workspaceId);
|
|
128354
|
+
for (const env of environments) {
|
|
128355
|
+
if (!isGcCandidateName(env.name)) continue;
|
|
128356
|
+
let marker;
|
|
128357
|
+
try {
|
|
128358
|
+
marker = markerFromEnvironment(await postman.getEnvironment(env.uid));
|
|
128359
|
+
} catch {
|
|
128360
|
+
marker = void 0;
|
|
128361
|
+
}
|
|
128362
|
+
candidates.push({ kind: "environment", uid: env.uid, name: env.name, marker });
|
|
128363
|
+
}
|
|
128364
|
+
const mocks = await postman.listMocks();
|
|
128365
|
+
for (const mock of mocks) {
|
|
128366
|
+
if (!isGcCandidateName(mock.name)) continue;
|
|
128367
|
+
const envMatch = candidates.find(
|
|
128368
|
+
(entry) => entry.kind === "environment" && entry.uid === mock.environment
|
|
128369
|
+
);
|
|
128370
|
+
candidates.push({ kind: "mock", uid: mock.uid, name: mock.name, marker: envMatch?.marker });
|
|
128371
|
+
}
|
|
128372
|
+
const monitors = await postman.listMonitors();
|
|
128373
|
+
for (const monitor of monitors) {
|
|
128374
|
+
if (!isGcCandidateName(monitor.name)) continue;
|
|
128375
|
+
const envMatch = candidates.find(
|
|
128376
|
+
(entry) => entry.kind === "environment" && entry.uid === monitor.environmentUid
|
|
128377
|
+
);
|
|
128378
|
+
candidates.push({ kind: "monitor", uid: monitor.uid, name: monitor.name, marker: envMatch?.marker });
|
|
128379
|
+
}
|
|
128380
|
+
const ownedCollections = /* @__PURE__ */ new Map();
|
|
128381
|
+
for (const mock of mocks) {
|
|
128382
|
+
const marker = candidates.find((entry) => entry.kind === "environment" && entry.uid === mock.environment)?.marker;
|
|
128383
|
+
if (marker && mock.collection) ownedCollections.set(mock.collection, { name: `${mock.name} collection`, marker });
|
|
128384
|
+
}
|
|
128385
|
+
for (const monitor of monitors) {
|
|
128386
|
+
const marker = candidates.find((entry) => entry.kind === "environment" && entry.uid === monitor.environmentUid)?.marker;
|
|
128387
|
+
if (marker && monitor.collectionUid) ownedCollections.set(monitor.collectionUid, { name: `${monitor.name} collection`, marker });
|
|
128388
|
+
}
|
|
128389
|
+
for (const [uid, collection] of ownedCollections) {
|
|
128390
|
+
candidates.push({ kind: "collection", uid, name: collection.name, marker: collection.marker });
|
|
128391
|
+
}
|
|
128392
|
+
const specifications = await postman.listSpecifications(workspaceId);
|
|
128393
|
+
for (const spec of specifications) {
|
|
128394
|
+
if (!isGcCandidateName(spec.name)) continue;
|
|
128395
|
+
let marker;
|
|
128396
|
+
try {
|
|
128397
|
+
marker = markerFromSpecContent(await postman.getSpecContent(spec.uid));
|
|
128398
|
+
} catch {
|
|
128399
|
+
marker = void 0;
|
|
128400
|
+
}
|
|
128401
|
+
candidates.push({ kind: "spec", uid: spec.uid, name: spec.name, marker });
|
|
128402
|
+
if (marker) {
|
|
128403
|
+
try {
|
|
128404
|
+
for (const collection of await postman.listSpecCollections(spec.uid)) {
|
|
128405
|
+
if (!ownedCollections.has(collection.uid)) {
|
|
128406
|
+
candidates.push({ kind: "collection", uid: collection.uid, name: collection.name || `${spec.name} collection`, marker });
|
|
128407
|
+
}
|
|
128408
|
+
}
|
|
128409
|
+
} catch {
|
|
128410
|
+
}
|
|
128411
|
+
}
|
|
128412
|
+
}
|
|
128413
|
+
return candidates;
|
|
128414
|
+
}
|
|
128415
|
+
async function runGc(options) {
|
|
128416
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
128417
|
+
const log = options.log ?? (() => void 0);
|
|
128418
|
+
const remoteBranches = options.onlyBranch || options.allPreviews ? void 0 : await inventoryRemoteBranches(options.exec);
|
|
128419
|
+
const degraded = remoteBranches === void 0 && !options.onlyBranch && !options.allPreviews;
|
|
128420
|
+
if (degraded) {
|
|
128421
|
+
log("gc: branch inventory unavailable (credential absent/denied?) \u2014 degraded sweep, TTL only");
|
|
128422
|
+
}
|
|
128423
|
+
const branchExists = (rawBranch) => {
|
|
128424
|
+
if (!remoteBranches) return "unknown";
|
|
128425
|
+
return remoteBranches.has(rawBranch) ? "exists" : "deleted";
|
|
128426
|
+
};
|
|
128427
|
+
const channelRules = options.channels === void 0 ? void 0 : parseChannelRules(options.channels);
|
|
128428
|
+
const channelMapped = channelRules ? (rawBranch) => channelRules.some(
|
|
128429
|
+
(rule) => rule.pattern.endsWith("*") ? rawBranch.startsWith(rule.pattern.slice(0, -1)) : rawBranch === rule.pattern
|
|
128430
|
+
) : void 0;
|
|
128431
|
+
const candidates = await collectGcCandidates(options.postman, options.workspaceId);
|
|
128432
|
+
const retiredChannels = candidates.filter((candidate) => candidate.kind === "environment" && candidate.marker?.role === "channel" && candidate.marker.retirementReason).map((candidate) => candidate.marker);
|
|
128433
|
+
for (const candidate of candidates) {
|
|
128434
|
+
const marker = candidate.marker;
|
|
128435
|
+
if (!marker || marker.role !== "channel" || marker.retirementReason) continue;
|
|
128436
|
+
const retired = retiredChannels.find((entry) => entry.repo === marker.repo && entry.rawBranch === marker.rawBranch);
|
|
128437
|
+
if (retired) candidate.marker = retired;
|
|
128438
|
+
}
|
|
128439
|
+
return runPreviewGc({
|
|
128440
|
+
context: {
|
|
128441
|
+
repo: options.repo,
|
|
128442
|
+
now,
|
|
128443
|
+
branchExists,
|
|
128444
|
+
channelMapped,
|
|
128445
|
+
onlyBranch: options.onlyBranch,
|
|
128446
|
+
allPreviews: options.allPreviews
|
|
128447
|
+
},
|
|
128448
|
+
candidates,
|
|
128449
|
+
deleters: {
|
|
128450
|
+
environment: (uid) => options.postman.deleteEnvironment(uid),
|
|
128451
|
+
mock: (uid) => options.postman.deleteMock(uid),
|
|
128452
|
+
monitor: (uid) => options.postman.deleteMonitor(uid),
|
|
128453
|
+
collection: (uid) => options.postman.deleteCollection(uid),
|
|
128454
|
+
spec: (uid) => options.postman.deleteSpec(uid)
|
|
128455
|
+
},
|
|
128456
|
+
retirers: {
|
|
128457
|
+
environment: async (candidate, reason) => {
|
|
128458
|
+
if (!candidate.marker) return;
|
|
128459
|
+
const envelope = await options.postman.getEnvironment(candidate.uid);
|
|
128460
|
+
const values = environmentValues(envelope);
|
|
128461
|
+
const retired = stampChannelRetirement(
|
|
128462
|
+
candidate.marker,
|
|
128463
|
+
reason,
|
|
128464
|
+
now,
|
|
128465
|
+
options.previewTtlDays ?? 30
|
|
128466
|
+
);
|
|
128467
|
+
const markerValue = values.find((value) => value.key === GC_MARKER_ENV_KEY);
|
|
128468
|
+
if (markerValue) markerValue.value = JSON.stringify(retired);
|
|
128469
|
+
else values.push({ key: GC_MARKER_ENV_KEY, value: JSON.stringify(retired), enabled: true, type: "default" });
|
|
128470
|
+
await options.postman.updateEnvironment(candidate.uid, candidate.name, values);
|
|
128471
|
+
},
|
|
128472
|
+
restoreEnvironment: async (candidate) => {
|
|
128473
|
+
if (!candidate.marker) return;
|
|
128474
|
+
const envelope = await options.postman.getEnvironment(candidate.uid);
|
|
128475
|
+
const values = environmentValues(envelope);
|
|
128476
|
+
const restored = clearChannelRetirement(candidate.marker);
|
|
128477
|
+
const markerValue = values.find((value) => value.key === GC_MARKER_ENV_KEY);
|
|
128478
|
+
if (markerValue) markerValue.value = JSON.stringify(restored);
|
|
128479
|
+
await options.postman.updateEnvironment(candidate.uid, candidate.name, values);
|
|
128480
|
+
}
|
|
128481
|
+
},
|
|
128482
|
+
degraded,
|
|
128483
|
+
dryRun: options.dryRun,
|
|
128484
|
+
log
|
|
128485
|
+
});
|
|
128486
|
+
}
|
|
127354
128487
|
|
|
127355
128488
|
// src/cli.ts
|
|
127356
128489
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -127399,15 +128532,25 @@ var CLI_INPUT_NAMES = [
|
|
|
127399
128532
|
"ssl-client-passphrase",
|
|
127400
128533
|
"ssl-extra-ca-certs",
|
|
127401
128534
|
"spec-id",
|
|
128535
|
+
"spec-content-changed",
|
|
127402
128536
|
"spec-path",
|
|
127403
128537
|
"team-id",
|
|
127404
128538
|
"postman-region",
|
|
127405
|
-
"postman-stack"
|
|
128539
|
+
"postman-stack",
|
|
128540
|
+
"branch-strategy",
|
|
128541
|
+
"canonical-branch",
|
|
128542
|
+
"channels",
|
|
128543
|
+
"preview-ttl"
|
|
127406
128544
|
];
|
|
127407
128545
|
var HELP_TEXT = `Usage: postman-repo-sync [options]
|
|
127408
128546
|
|
|
127409
128547
|
Sync Postman artifacts into a git repository.
|
|
127410
128548
|
|
|
128549
|
+
Subcommands:
|
|
128550
|
+
gc [--branch <name> | --all-previews] [--dry-run]
|
|
128551
|
+
Garbage-collect preview/channel asset sets
|
|
128552
|
+
(marker-guarded; strangers are never deleted)
|
|
128553
|
+
|
|
127411
128554
|
Options:
|
|
127412
128555
|
--help Show this help and exit
|
|
127413
128556
|
--version Show version and exit
|
|
@@ -127501,7 +128644,7 @@ function resolvePackageVersion() {
|
|
|
127501
128644
|
candidates.push(import_node_path3.default.join(process.cwd(), "package.json"));
|
|
127502
128645
|
for (const packageJsonPath of candidates) {
|
|
127503
128646
|
try {
|
|
127504
|
-
const packageJson = JSON.parse((0,
|
|
128647
|
+
const packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf8"));
|
|
127505
128648
|
if (packageJson.name === "@postman-cse/onboarding-repo-sync" && packageJson.version) {
|
|
127506
128649
|
return String(packageJson.version).trim();
|
|
127507
128650
|
}
|
|
@@ -127666,9 +128809,22 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
127666
128809
|
return;
|
|
127667
128810
|
}
|
|
127668
128811
|
const env = runtime.env ?? process.env;
|
|
128812
|
+
if (argv[0] === "gc") {
|
|
128813
|
+
await runGcCommand(argv.slice(1), env, writeStdout);
|
|
128814
|
+
return;
|
|
128815
|
+
}
|
|
127669
128816
|
const config = parseCliArgs(argv, env);
|
|
127670
128817
|
const inputs = resolveInputs(config.inputEnv);
|
|
127671
128818
|
const reporter = new ConsoleReporter();
|
|
128819
|
+
const branchDecision = decideBranchTier(inputs, config.inputEnv);
|
|
128820
|
+
if (branchDecision.tier === "gated") {
|
|
128821
|
+
const result2 = runGatedSkip(inputs, branchDecision, reporter);
|
|
128822
|
+
await writeOptionalFile(config.resultJsonPath, JSON.stringify(result2, null, 2));
|
|
128823
|
+
await writeOptionalFile(config.dotenvPath, toDotenv(result2));
|
|
128824
|
+
writeStdout(`${JSON.stringify(result2, null, 2)}
|
|
128825
|
+
`);
|
|
128826
|
+
return;
|
|
128827
|
+
}
|
|
127672
128828
|
await mintAccessTokenIfNeeded(inputs, reporter);
|
|
127673
128829
|
const initialMasker = createSecretMasker([
|
|
127674
128830
|
inputs.postmanApiKey,
|
|
@@ -127719,6 +128875,80 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
127719
128875
|
writeStdout(`${JSON.stringify(result, null, 2)}
|
|
127720
128876
|
`);
|
|
127721
128877
|
}
|
|
128878
|
+
async function runGcCommand(argv, env, writeStdout) {
|
|
128879
|
+
let onlyBranch;
|
|
128880
|
+
let allPreviews = false;
|
|
128881
|
+
let dryRun = false;
|
|
128882
|
+
const passthrough = [];
|
|
128883
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
128884
|
+
const arg = argv[index];
|
|
128885
|
+
if (arg === "--branch") {
|
|
128886
|
+
onlyBranch = argv[index + 1];
|
|
128887
|
+
if (!onlyBranch || onlyBranch.startsWith("--")) {
|
|
128888
|
+
throw new Error("Missing value for --branch");
|
|
128889
|
+
}
|
|
128890
|
+
index += 1;
|
|
128891
|
+
} else if (arg === "--all-previews") {
|
|
128892
|
+
allPreviews = true;
|
|
128893
|
+
} else if (arg === "--dry-run") {
|
|
128894
|
+
dryRun = true;
|
|
128895
|
+
} else {
|
|
128896
|
+
passthrough.push(arg);
|
|
128897
|
+
}
|
|
128898
|
+
}
|
|
128899
|
+
if (onlyBranch && allPreviews) {
|
|
128900
|
+
throw new Error("Use either --branch <name> or --all-previews, not both");
|
|
128901
|
+
}
|
|
128902
|
+
const config = parseCliArgs(passthrough, env);
|
|
128903
|
+
const inputs = resolveInputs(config.inputEnv);
|
|
128904
|
+
if (!inputs.workspaceId) {
|
|
128905
|
+
throw new Error("gc requires --workspace-id (the workspace holding the preview/channel sets)");
|
|
128906
|
+
}
|
|
128907
|
+
const repo = inputs.repoUrl || inputs.repository;
|
|
128908
|
+
if (!repo) {
|
|
128909
|
+
throw new Error("gc requires --repo-url or --repository to scope marker ownership");
|
|
128910
|
+
}
|
|
128911
|
+
const reporter = new ConsoleReporter();
|
|
128912
|
+
await mintAccessTokenIfNeeded(inputs, reporter);
|
|
128913
|
+
const initialMasker = createSecretMasker([
|
|
128914
|
+
inputs.postmanApiKey,
|
|
128915
|
+
inputs.postmanAccessToken,
|
|
128916
|
+
inputs.githubToken,
|
|
128917
|
+
inputs.ghFallbackToken
|
|
128918
|
+
]);
|
|
128919
|
+
const resolved = await resolvePostmanApiKeyAndTeamId(
|
|
128920
|
+
inputs,
|
|
128921
|
+
reporter,
|
|
128922
|
+
createCliExec(initialMasker),
|
|
128923
|
+
initialMasker,
|
|
128924
|
+
{ persistGeneratedApiKeySecret: false, env }
|
|
128925
|
+
);
|
|
128926
|
+
const dependencies = createCliDependencies(inputs, resolved);
|
|
128927
|
+
if (!dependencies.postman.deleteCollection || !dependencies.postman.listSpecifications || !dependencies.postman.getSpecContent || !dependencies.postman.listSpecCollections || !dependencies.postman.deleteSpec) {
|
|
128928
|
+
throw new Error("gc requires the full branch-aware inventory client; this runtime is missing a spec or collection GC capability.");
|
|
128929
|
+
}
|
|
128930
|
+
const summary2 = await runGc({
|
|
128931
|
+
workspaceId: inputs.workspaceId,
|
|
128932
|
+
repo,
|
|
128933
|
+
postman: {
|
|
128934
|
+
...dependencies.postman,
|
|
128935
|
+
deleteCollection: dependencies.postman.deleteCollection,
|
|
128936
|
+
listSpecifications: dependencies.postman.listSpecifications,
|
|
128937
|
+
getSpecContent: dependencies.postman.getSpecContent,
|
|
128938
|
+
listSpecCollections: dependencies.postman.listSpecCollections,
|
|
128939
|
+
deleteSpec: dependencies.postman.deleteSpec
|
|
128940
|
+
},
|
|
128941
|
+
exec: createCliExec(initialMasker),
|
|
128942
|
+
onlyBranch,
|
|
128943
|
+
allPreviews,
|
|
128944
|
+
dryRun,
|
|
128945
|
+
previewTtlDays: inputs.previewTtlDays,
|
|
128946
|
+
channels: inputs.channels,
|
|
128947
|
+
log: (message) => reporter.info(message)
|
|
128948
|
+
});
|
|
128949
|
+
reporter.info(renderGcSummary(summary2));
|
|
128950
|
+
writeStdout(JSON.stringify(summary2, null, 2) + "\n");
|
|
128951
|
+
}
|
|
127722
128952
|
var currentModulePath = typeof __filename === "string" ? __filename : "";
|
|
127723
128953
|
var entrypoint = process.argv[1];
|
|
127724
128954
|
function isEntrypoint(currentPath, entrypointPath) {
|
|
@@ -127726,7 +128956,7 @@ function isEntrypoint(currentPath, entrypointPath) {
|
|
|
127726
128956
|
return false;
|
|
127727
128957
|
}
|
|
127728
128958
|
try {
|
|
127729
|
-
return (0,
|
|
128959
|
+
return (0, import_node_fs5.realpathSync)(currentPath) === (0, import_node_fs5.realpathSync)(entrypointPath);
|
|
127730
128960
|
} catch {
|
|
127731
128961
|
return import_node_path3.default.resolve(currentPath) === import_node_path3.default.resolve(entrypointPath);
|
|
127732
128962
|
}
|