@postman-cse/onboarding-repo-sync 2.1.5 → 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 +866 -104
- package/dist/cli.cjs +1322 -108
- package/dist/index.cjs +876 -104
- package/package.json +1 -1
package/dist/action.cjs
CHANGED
|
@@ -92333,11 +92333,11 @@ var require_valid = __commonJS({
|
|
|
92333
92333
|
var require_clean = __commonJS({
|
|
92334
92334
|
"node_modules/postman-collection/node_modules/semver/functions/clean.js"(exports2, module2) {
|
|
92335
92335
|
var parse2 = require_parse5();
|
|
92336
|
-
var
|
|
92336
|
+
var clean2 = (version2, options) => {
|
|
92337
92337
|
const s = parse2(version2.trim().replace(/^[=v]+/, ""), options);
|
|
92338
92338
|
return s ? s.version : null;
|
|
92339
92339
|
};
|
|
92340
|
-
module2.exports =
|
|
92340
|
+
module2.exports = clean2;
|
|
92341
92341
|
}
|
|
92342
92342
|
});
|
|
92343
92343
|
|
|
@@ -93648,7 +93648,7 @@ var require_semver2 = __commonJS({
|
|
|
93648
93648
|
var identifiers = require_identifiers();
|
|
93649
93649
|
var parse2 = require_parse5();
|
|
93650
93650
|
var valid = require_valid();
|
|
93651
|
-
var
|
|
93651
|
+
var clean2 = require_clean();
|
|
93652
93652
|
var inc = require_inc();
|
|
93653
93653
|
var diff = require_diff();
|
|
93654
93654
|
var major = require_major();
|
|
@@ -93686,7 +93686,7 @@ var require_semver2 = __commonJS({
|
|
|
93686
93686
|
module2.exports = {
|
|
93687
93687
|
parse: parse2,
|
|
93688
93688
|
valid,
|
|
93689
|
-
clean,
|
|
93689
|
+
clean: clean2,
|
|
93690
93690
|
inc,
|
|
93691
93691
|
diff,
|
|
93692
93692
|
major,
|
|
@@ -122063,7 +122063,7 @@ function getIDToken(aud) {
|
|
|
122063
122063
|
}
|
|
122064
122064
|
|
|
122065
122065
|
// src/index.ts
|
|
122066
|
-
var
|
|
122066
|
+
var import_node_fs4 = require("node:fs");
|
|
122067
122067
|
var path8 = __toESM(require("node:path"), 1);
|
|
122068
122068
|
|
|
122069
122069
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -125186,6 +125186,9 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125186
125186
|
" branches: [main]",
|
|
125187
125187
|
" schedule:",
|
|
125188
125188
|
' - cron: "0 */6 * * *"',
|
|
125189
|
+
"concurrency:",
|
|
125190
|
+
" group: postman-onboard-${{ github.head_ref || github.ref_name }}",
|
|
125191
|
+
" cancel-in-progress: false",
|
|
125189
125192
|
"jobs:",
|
|
125190
125193
|
" test:",
|
|
125191
125194
|
" runs-on: ubuntu-latest",
|
|
@@ -125202,7 +125205,7 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125202
125205
|
" ruby <<'RUBY'",
|
|
125203
125206
|
" require 'yaml'",
|
|
125204
125207
|
" config = YAML.load_file('.postman/resources.yaml') || {}",
|
|
125205
|
-
" cloud = config.fetch('cloudResources', {})",
|
|
125208
|
+
" cloud = config.fetch('canonical', config.fetch('cloudResources', {}))",
|
|
125206
125209
|
" collections = cloud.fetch('collections', {})",
|
|
125207
125210
|
" environments = cloud.fetch('environments', {})",
|
|
125208
125211
|
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
@@ -125400,6 +125403,58 @@ function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125400
125403
|
""
|
|
125401
125404
|
];
|
|
125402
125405
|
}
|
|
125406
|
+
function renderGcWorkflowTemplate() {
|
|
125407
|
+
return [
|
|
125408
|
+
"name: Postman Preview GC",
|
|
125409
|
+
"on:",
|
|
125410
|
+
" delete:",
|
|
125411
|
+
' branches: ["**"]',
|
|
125412
|
+
" pull_request:",
|
|
125413
|
+
" types: [closed]",
|
|
125414
|
+
" schedule:",
|
|
125415
|
+
' - cron: "0 2 * * *"',
|
|
125416
|
+
" workflow_dispatch:",
|
|
125417
|
+
" inputs:",
|
|
125418
|
+
" branch:",
|
|
125419
|
+
" description: Branch name to GC (optional, otherwise sweep by TTL/branch-existence)",
|
|
125420
|
+
" required: false",
|
|
125421
|
+
"concurrency:",
|
|
125422
|
+
" group: postman-preview-gc-${{ github.ref_name }}",
|
|
125423
|
+
" cancel-in-progress: false",
|
|
125424
|
+
"jobs:",
|
|
125425
|
+
" gc:",
|
|
125426
|
+
" runs-on: ubuntu-latest",
|
|
125427
|
+
" if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
125428
|
+
" steps:",
|
|
125429
|
+
" - uses: actions/checkout@v5",
|
|
125430
|
+
" with:",
|
|
125431
|
+
" fetch-depth: 0",
|
|
125432
|
+
" - name: Run Postman preview GC (provider-neutral cli.cjs gc)",
|
|
125433
|
+
" env:",
|
|
125434
|
+
" POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}",
|
|
125435
|
+
" POSTMAN_ACCESS_TOKEN: ${{ secrets.POSTMAN_ACCESS_TOKEN }}",
|
|
125436
|
+
" POSTMAN_WORKSPACE_ID: ${{ vars.POSTMAN_WORKSPACE_ID }}",
|
|
125437
|
+
" REPO_URL: https://github.com/${{ github.repository }}",
|
|
125438
|
+
" run: |",
|
|
125439
|
+
" set -euo pipefail",
|
|
125440
|
+
" # The repo-sync action bundles a provider-neutral gc command (cli.cjs gc)",
|
|
125441
|
+
" # that uses the Postman access token for inventory/deletion and the",
|
|
125442
|
+
" # provider ambient credential (GITHUB_TOKEN via git ls-remote) for branch existence.",
|
|
125443
|
+
" # Daily scheduled run is the retention executor (TTL contract of last resort).",
|
|
125444
|
+
' if [ -n "${{ inputs.branch }}" ]; then',
|
|
125445
|
+
' npx @postman-cse/onboarding-repo-sync gc --branch "${{ inputs.branch }}" --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125446
|
+
" else",
|
|
125447
|
+
' npx @postman-cse/onboarding-repo-sync gc --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125448
|
+
" fi",
|
|
125449
|
+
" - name: Orphan audit summary (job summary)",
|
|
125450
|
+
" if: always()",
|
|
125451
|
+
" run: |",
|
|
125452
|
+
' echo "### Preview GC orphan audit" >> "$GITHUB_STEP_SUMMARY"',
|
|
125453
|
+
' echo "Marker-guarded deletion only \u2014 strangers (no marker) are never deleted. See gc-summary-json for structured counts." >> "$GITHUB_STEP_SUMMARY"',
|
|
125454
|
+
""
|
|
125455
|
+
].join("\n");
|
|
125456
|
+
}
|
|
125457
|
+
var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
|
|
125403
125458
|
function getCiWorkflowTemplate(provider, options = {}) {
|
|
125404
125459
|
if (provider === "azure-devops") {
|
|
125405
125460
|
const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
|
|
@@ -126970,6 +127025,53 @@ function createInternalIntegrationAdapter(options) {
|
|
|
126970
127025
|
return new BifrostInternalIntegrationAdapter(options);
|
|
126971
127026
|
}
|
|
126972
127027
|
|
|
127028
|
+
// src/lib/retry.ts
|
|
127029
|
+
function sleep(delayMs) {
|
|
127030
|
+
return new Promise((resolve3) => {
|
|
127031
|
+
setTimeout(resolve3, delayMs);
|
|
127032
|
+
});
|
|
127033
|
+
}
|
|
127034
|
+
function normalizeRetryOptions(options) {
|
|
127035
|
+
return {
|
|
127036
|
+
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
127037
|
+
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
127038
|
+
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
127039
|
+
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
127040
|
+
onRetry: options.onRetry ?? (async () => void 0),
|
|
127041
|
+
shouldRetry: options.shouldRetry ?? (() => true),
|
|
127042
|
+
sleep: options.sleep ?? sleep
|
|
127043
|
+
};
|
|
127044
|
+
}
|
|
127045
|
+
async function retry(operation, options = {}) {
|
|
127046
|
+
const normalized = normalizeRetryOptions(options);
|
|
127047
|
+
let nextDelayMs = normalized.delayMs;
|
|
127048
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
127049
|
+
try {
|
|
127050
|
+
return await operation();
|
|
127051
|
+
} catch (error2) {
|
|
127052
|
+
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
127053
|
+
attempt,
|
|
127054
|
+
maxAttempts: normalized.maxAttempts
|
|
127055
|
+
});
|
|
127056
|
+
if (!shouldRetry) {
|
|
127057
|
+
throw error2;
|
|
127058
|
+
}
|
|
127059
|
+
await normalized.onRetry({
|
|
127060
|
+
attempt,
|
|
127061
|
+
maxAttempts: normalized.maxAttempts,
|
|
127062
|
+
delayMs: nextDelayMs,
|
|
127063
|
+
error: error2
|
|
127064
|
+
});
|
|
127065
|
+
await normalized.sleep(nextDelayMs);
|
|
127066
|
+
nextDelayMs = Math.min(
|
|
127067
|
+
normalized.maxDelayMs,
|
|
127068
|
+
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
127069
|
+
);
|
|
127070
|
+
}
|
|
127071
|
+
}
|
|
127072
|
+
throw new Error("Retry exhausted without returning or throwing");
|
|
127073
|
+
}
|
|
127074
|
+
|
|
126973
127075
|
// src/contracts.ts
|
|
126974
127076
|
var postmanRepoSyncActionContract = {
|
|
126975
127077
|
name: "postman-repo-sync-action",
|
|
@@ -127139,6 +127241,25 @@ var postmanRepoSyncActionContract = {
|
|
|
127139
127241
|
default: "warn",
|
|
127140
127242
|
allowedValues: ["enforce", "warn"]
|
|
127141
127243
|
},
|
|
127244
|
+
"branch-strategy": {
|
|
127245
|
+
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.",
|
|
127246
|
+
required: false,
|
|
127247
|
+
default: "legacy",
|
|
127248
|
+
allowedValues: ["legacy", "preview", "publish-gate"]
|
|
127249
|
+
},
|
|
127250
|
+
"canonical-branch": {
|
|
127251
|
+
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.",
|
|
127252
|
+
required: false
|
|
127253
|
+
},
|
|
127254
|
+
"channels": {
|
|
127255
|
+
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.',
|
|
127256
|
+
required: false
|
|
127257
|
+
},
|
|
127258
|
+
"preview-ttl": {
|
|
127259
|
+
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).",
|
|
127260
|
+
required: false,
|
|
127261
|
+
default: "30"
|
|
127262
|
+
},
|
|
127142
127263
|
"github-token": {
|
|
127143
127264
|
description: "GitHub token used for repo variable persistence and commits.",
|
|
127144
127265
|
required: false
|
|
@@ -127176,6 +127297,11 @@ var postmanRepoSyncActionContract = {
|
|
|
127176
127297
|
description: "Spec UID from bootstrap, persisted into .postman/resources.yaml cloudResources.",
|
|
127177
127298
|
required: false
|
|
127178
127299
|
},
|
|
127300
|
+
"spec-content-changed": {
|
|
127301
|
+
description: "Whether bootstrap changed canonical spec content; controls final native Spec Hub tag publication.",
|
|
127302
|
+
required: false,
|
|
127303
|
+
default: "true"
|
|
127304
|
+
},
|
|
127179
127305
|
"spec-path": {
|
|
127180
127306
|
description: "Optional repo-root-relative path to the local spec file for resources/workflows metadata.",
|
|
127181
127307
|
required: false
|
|
@@ -127220,6 +127346,18 @@ var postmanRepoSyncActionContract = {
|
|
|
127220
127346
|
},
|
|
127221
127347
|
"commit-sha": {
|
|
127222
127348
|
description: "Commit SHA produced by repo-write-mode, if any."
|
|
127349
|
+
},
|
|
127350
|
+
"sync-status": {
|
|
127351
|
+
description: "Branch-aware sync status: synced, skipped-branch-gate, or empty under branch-strategy legacy."
|
|
127352
|
+
},
|
|
127353
|
+
"branch-decision": {
|
|
127354
|
+
description: "Serialized BranchDecision JSON for downstream actions (also exported as POSTMAN_BRANCH_DECISION)."
|
|
127355
|
+
},
|
|
127356
|
+
"spec-version-tag": {
|
|
127357
|
+
description: "Native Spec Hub version tag created after successful canonical repo-sync finalization."
|
|
127358
|
+
},
|
|
127359
|
+
"spec-version-url": {
|
|
127360
|
+
description: "Read-only URL for the tagged Spec Hub snapshot."
|
|
127223
127361
|
}
|
|
127224
127362
|
},
|
|
127225
127363
|
behavior: {
|
|
@@ -127287,53 +127425,6 @@ var PostmanAssetsClient = class {
|
|
|
127287
127425
|
}
|
|
127288
127426
|
};
|
|
127289
127427
|
|
|
127290
|
-
// src/lib/retry.ts
|
|
127291
|
-
function sleep(delayMs) {
|
|
127292
|
-
return new Promise((resolve3) => {
|
|
127293
|
-
setTimeout(resolve3, delayMs);
|
|
127294
|
-
});
|
|
127295
|
-
}
|
|
127296
|
-
function normalizeRetryOptions(options) {
|
|
127297
|
-
return {
|
|
127298
|
-
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
127299
|
-
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
127300
|
-
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
127301
|
-
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
127302
|
-
onRetry: options.onRetry ?? (async () => void 0),
|
|
127303
|
-
shouldRetry: options.shouldRetry ?? (() => true),
|
|
127304
|
-
sleep: options.sleep ?? sleep
|
|
127305
|
-
};
|
|
127306
|
-
}
|
|
127307
|
-
async function retry(operation, options = {}) {
|
|
127308
|
-
const normalized = normalizeRetryOptions(options);
|
|
127309
|
-
let nextDelayMs = normalized.delayMs;
|
|
127310
|
-
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
127311
|
-
try {
|
|
127312
|
-
return await operation();
|
|
127313
|
-
} catch (error2) {
|
|
127314
|
-
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
127315
|
-
attempt,
|
|
127316
|
-
maxAttempts: normalized.maxAttempts
|
|
127317
|
-
});
|
|
127318
|
-
if (!shouldRetry) {
|
|
127319
|
-
throw error2;
|
|
127320
|
-
}
|
|
127321
|
-
await normalized.onRetry({
|
|
127322
|
-
attempt,
|
|
127323
|
-
maxAttempts: normalized.maxAttempts,
|
|
127324
|
-
delayMs: nextDelayMs,
|
|
127325
|
-
error: error2
|
|
127326
|
-
});
|
|
127327
|
-
await normalized.sleep(nextDelayMs);
|
|
127328
|
-
nextDelayMs = Math.min(
|
|
127329
|
-
normalized.maxDelayMs,
|
|
127330
|
-
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
127331
|
-
);
|
|
127332
|
-
}
|
|
127333
|
-
}
|
|
127334
|
-
throw new Error("Retry exhausted without returning or throwing");
|
|
127335
|
-
}
|
|
127336
|
-
|
|
127337
127428
|
// src/lib/postman/postman-gateway-assets-client.ts
|
|
127338
127429
|
var MAX_CREATE_FLIGHTS = 256;
|
|
127339
127430
|
var createFlights = /* @__PURE__ */ new Map();
|
|
@@ -127359,6 +127450,84 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127359
127450
|
if (!envelope) return null;
|
|
127360
127451
|
return this.asRecord(envelope.data) ?? envelope;
|
|
127361
127452
|
}
|
|
127453
|
+
/** Native Spec Hub tags attach to the latest changelog group. */
|
|
127454
|
+
async tagSpecVersion(specId, name) {
|
|
127455
|
+
const trimmed = name.trim().slice(0, 255);
|
|
127456
|
+
const response = await this.gateway.requestJson({
|
|
127457
|
+
service: "specification",
|
|
127458
|
+
method: "post",
|
|
127459
|
+
path: `/specifications/${specId}/tags`,
|
|
127460
|
+
body: { name: trimmed }
|
|
127461
|
+
});
|
|
127462
|
+
const record = this.dataOf(response) ?? {};
|
|
127463
|
+
return { id: String(record.id ?? "").trim(), name: String(record.name ?? trimmed).trim() };
|
|
127464
|
+
}
|
|
127465
|
+
async listSpecVersionTags(specId) {
|
|
127466
|
+
const response = await this.gateway.requestJson({
|
|
127467
|
+
service: "specification",
|
|
127468
|
+
method: "get",
|
|
127469
|
+
path: `/specifications/${specId}/tags`,
|
|
127470
|
+
query: { limit: "50" }
|
|
127471
|
+
});
|
|
127472
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127473
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({
|
|
127474
|
+
id: String(entry.id ?? "").trim(),
|
|
127475
|
+
// listTags returns `message`; createTag returns `name`. Accept both.
|
|
127476
|
+
name: String(entry.name ?? entry.message ?? "").trim()
|
|
127477
|
+
})).filter((entry) => entry.id || entry.name);
|
|
127478
|
+
}
|
|
127479
|
+
async deleteCollection(collectionUid) {
|
|
127480
|
+
const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
|
|
127481
|
+
await this.gateway.requestJson({
|
|
127482
|
+
service: "collection",
|
|
127483
|
+
method: "delete",
|
|
127484
|
+
path: `/v3/collections/${bareId}`
|
|
127485
|
+
});
|
|
127486
|
+
}
|
|
127487
|
+
async listSpecifications(workspaceId) {
|
|
127488
|
+
const response = await this.gateway.requestJson({
|
|
127489
|
+
service: "specification",
|
|
127490
|
+
method: "get",
|
|
127491
|
+
path: `/specifications?containerType=workspace&containerId=${workspaceId}`
|
|
127492
|
+
});
|
|
127493
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127494
|
+
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);
|
|
127495
|
+
}
|
|
127496
|
+
async getSpecContent(specId) {
|
|
127497
|
+
const files = await this.gateway.requestJson({
|
|
127498
|
+
service: "specification",
|
|
127499
|
+
method: "get",
|
|
127500
|
+
path: `/specifications/${specId}/files`
|
|
127501
|
+
});
|
|
127502
|
+
const entries = Array.isArray(files?.data) ? files.data : [];
|
|
127503
|
+
const root = entries.map((entry) => this.asRecord(entry)).find((entry) => entry?.type === "ROOT") ?? entries.map((entry) => this.asRecord(entry)).find((entry) => entry !== null);
|
|
127504
|
+
const fileId = String(root?.id ?? "").trim();
|
|
127505
|
+
if (!fileId) return void 0;
|
|
127506
|
+
const file = await this.gateway.requestJson({
|
|
127507
|
+
service: "specification",
|
|
127508
|
+
method: "get",
|
|
127509
|
+
path: `/specifications/${specId}/files/${fileId}`,
|
|
127510
|
+
query: { fields: "content" }
|
|
127511
|
+
});
|
|
127512
|
+
const record = this.dataOf(file);
|
|
127513
|
+
return typeof record?.content === "string" ? record.content : void 0;
|
|
127514
|
+
}
|
|
127515
|
+
async listSpecCollections(specId) {
|
|
127516
|
+
const response = await this.gateway.requestJson({
|
|
127517
|
+
service: "specification",
|
|
127518
|
+
method: "get",
|
|
127519
|
+
path: `/specifications/${specId}/collections`
|
|
127520
|
+
});
|
|
127521
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127522
|
+
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);
|
|
127523
|
+
}
|
|
127524
|
+
async deleteSpec(specId) {
|
|
127525
|
+
await this.gateway.requestJson({
|
|
127526
|
+
service: "specification",
|
|
127527
|
+
method: "delete",
|
|
127528
|
+
path: `/specifications/${specId}`
|
|
127529
|
+
});
|
|
127530
|
+
}
|
|
127362
127531
|
idOf(record) {
|
|
127363
127532
|
if (!record) return "";
|
|
127364
127533
|
const id = record.uid ?? record.id;
|
|
@@ -127643,6 +127812,9 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127643
127812
|
path: `/mocks?workspace=${ws}`,
|
|
127644
127813
|
body
|
|
127645
127814
|
},
|
|
127815
|
+
// Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
|
|
127816
|
+
// may still have created the mock; reconcile via discovery below and
|
|
127817
|
+
// let the orchestrator retry the whole create-or-adopt cycle.
|
|
127646
127818
|
{ retryTransient: false }
|
|
127647
127819
|
);
|
|
127648
127820
|
const record = this.dataOf(response);
|
|
@@ -127681,6 +127853,33 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127681
127853
|
environment: String(m.environment ?? "")
|
|
127682
127854
|
}));
|
|
127683
127855
|
}
|
|
127856
|
+
/**
|
|
127857
|
+
* Delete an environment through the sync service (GC path). The path id is
|
|
127858
|
+
* the bare model id (public uid tail), mirroring updateEnvironment.
|
|
127859
|
+
*/
|
|
127860
|
+
async deleteEnvironment(uid) {
|
|
127861
|
+
await this.gateway.requestJson({
|
|
127862
|
+
service: "sync",
|
|
127863
|
+
method: "delete",
|
|
127864
|
+
path: `/environment/${this.toModelId(uid)}`
|
|
127865
|
+
});
|
|
127866
|
+
}
|
|
127867
|
+
/** Delete a mock server (GC path). */
|
|
127868
|
+
async deleteMock(uid) {
|
|
127869
|
+
await this.gateway.requestJson({
|
|
127870
|
+
service: "mock",
|
|
127871
|
+
method: "delete",
|
|
127872
|
+
path: `/mocks/${this.toModelId(uid)}`
|
|
127873
|
+
});
|
|
127874
|
+
}
|
|
127875
|
+
/** Delete a collection-based monitor (jobTemplate) (GC path). */
|
|
127876
|
+
async deleteMonitor(uid) {
|
|
127877
|
+
await this.gateway.requestJson({
|
|
127878
|
+
service: "monitors",
|
|
127879
|
+
method: "delete",
|
|
127880
|
+
path: `/jobTemplates/${this.toModelId(uid)}`
|
|
127881
|
+
});
|
|
127882
|
+
}
|
|
127684
127883
|
async mockExists(uid) {
|
|
127685
127884
|
try {
|
|
127686
127885
|
await this.gateway.requestJson({
|
|
@@ -128187,6 +128386,331 @@ function validateCertMaterial(certBase64, keyBase64, passphrase) {
|
|
|
128187
128386
|
}
|
|
128188
128387
|
}
|
|
128189
128388
|
|
|
128389
|
+
// src/lib/repo/branch-decision.ts
|
|
128390
|
+
var import_node_fs3 = require("node:fs");
|
|
128391
|
+
var import_node_crypto3 = require("node:crypto");
|
|
128392
|
+
var ContractError = class extends Error {
|
|
128393
|
+
code;
|
|
128394
|
+
constructor(code, message) {
|
|
128395
|
+
super(`${code}: ${message}`);
|
|
128396
|
+
this.code = code;
|
|
128397
|
+
this.name = "ContractError";
|
|
128398
|
+
}
|
|
128399
|
+
};
|
|
128400
|
+
function clean(value) {
|
|
128401
|
+
const trimmed = (value ?? "").trim();
|
|
128402
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
128403
|
+
}
|
|
128404
|
+
function stripRefPrefix(ref) {
|
|
128405
|
+
const raw = clean(ref);
|
|
128406
|
+
if (!raw) {
|
|
128407
|
+
return { kind: "unknown" };
|
|
128408
|
+
}
|
|
128409
|
+
if (raw.startsWith("refs/heads/")) {
|
|
128410
|
+
return { name: raw.slice("refs/heads/".length), kind: "branch" };
|
|
128411
|
+
}
|
|
128412
|
+
if (raw.startsWith("refs/tags/")) {
|
|
128413
|
+
return { name: raw.slice("refs/tags/".length), kind: "tag" };
|
|
128414
|
+
}
|
|
128415
|
+
if (raw.startsWith("refs/pull/") || raw.startsWith("refs/merge")) {
|
|
128416
|
+
return { kind: "unknown" };
|
|
128417
|
+
}
|
|
128418
|
+
return { name: raw, kind: "branch" };
|
|
128419
|
+
}
|
|
128420
|
+
function detectProvider(env) {
|
|
128421
|
+
if (clean(env.GITHUB_ACTIONS) || clean(env.GITHUB_REPOSITORY)) return "github";
|
|
128422
|
+
if (clean(env.GITLAB_CI) || clean(env.CI_PROJECT_PATH)) return "gitlab";
|
|
128423
|
+
if (clean(env.BITBUCKET_REPO_SLUG) || clean(env.BITBUCKET_BRANCH)) return "bitbucket";
|
|
128424
|
+
if (clean(env.TF_BUILD) || clean(env.BUILD_REPOSITORY_URI)) return "azure-devops";
|
|
128425
|
+
return "unknown";
|
|
128426
|
+
}
|
|
128427
|
+
function readGithubEvent(env) {
|
|
128428
|
+
const path9 = clean(env.GITHUB_EVENT_PATH);
|
|
128429
|
+
if (!path9) return void 0;
|
|
128430
|
+
try {
|
|
128431
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(path9, "utf8"));
|
|
128432
|
+
} catch {
|
|
128433
|
+
return void 0;
|
|
128434
|
+
}
|
|
128435
|
+
}
|
|
128436
|
+
function resolveBranchIdentity(env = process.env, overrides = {}) {
|
|
128437
|
+
const provider = detectProvider(env);
|
|
128438
|
+
const explicitDefault = clean(overrides.defaultBranch);
|
|
128439
|
+
let headBranch;
|
|
128440
|
+
let rawRef;
|
|
128441
|
+
let refKind;
|
|
128442
|
+
let isPrContext = false;
|
|
128443
|
+
let isForkPr = false;
|
|
128444
|
+
let defaultBranch = explicitDefault;
|
|
128445
|
+
let headSha;
|
|
128446
|
+
switch (provider) {
|
|
128447
|
+
case "github": {
|
|
128448
|
+
const event = readGithubEvent(env);
|
|
128449
|
+
headSha = clean(env.GITHUB_SHA);
|
|
128450
|
+
defaultBranch ??= clean(event?.repository?.default_branch);
|
|
128451
|
+
const headRef = clean(env.GITHUB_HEAD_REF);
|
|
128452
|
+
if (headRef) {
|
|
128453
|
+
isPrContext = true;
|
|
128454
|
+
headBranch = headRef;
|
|
128455
|
+
rawRef = clean(env.GITHUB_REF) ?? headRef;
|
|
128456
|
+
refKind = "branch";
|
|
128457
|
+
const headRepo = event?.pull_request?.head?.repo?.full_name;
|
|
128458
|
+
const baseRepo = event?.pull_request?.base?.repo?.full_name ?? event?.repository?.full_name;
|
|
128459
|
+
isForkPr = Boolean(headRepo && baseRepo && headRepo !== baseRepo);
|
|
128460
|
+
headSha = clean(event?.pull_request?.head?.sha) ?? headSha;
|
|
128461
|
+
} else {
|
|
128462
|
+
rawRef = clean(env.GITHUB_REF) ?? clean(env.GITHUB_REF_NAME);
|
|
128463
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128464
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128465
|
+
refKind = parsed.kind;
|
|
128466
|
+
}
|
|
128467
|
+
break;
|
|
128468
|
+
}
|
|
128469
|
+
case "gitlab": {
|
|
128470
|
+
headSha = clean(env.CI_COMMIT_SHA);
|
|
128471
|
+
defaultBranch ??= clean(env.CI_DEFAULT_BRANCH);
|
|
128472
|
+
const mrSource = clean(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME);
|
|
128473
|
+
if (mrSource) {
|
|
128474
|
+
isPrContext = true;
|
|
128475
|
+
headBranch = mrSource;
|
|
128476
|
+
rawRef = mrSource;
|
|
128477
|
+
refKind = "branch";
|
|
128478
|
+
const sourceProject = clean(env.CI_MERGE_REQUEST_SOURCE_PROJECT_ID);
|
|
128479
|
+
const targetProject = clean(env.CI_MERGE_REQUEST_PROJECT_ID);
|
|
128480
|
+
isForkPr = Boolean(sourceProject && targetProject && sourceProject !== targetProject);
|
|
128481
|
+
} else if (clean(env.CI_COMMIT_TAG)) {
|
|
128482
|
+
rawRef = clean(env.CI_COMMIT_TAG);
|
|
128483
|
+
refKind = "tag";
|
|
128484
|
+
} else {
|
|
128485
|
+
headBranch = clean(env.CI_COMMIT_BRANCH) ?? clean(env.CI_COMMIT_REF_NAME);
|
|
128486
|
+
rawRef = headBranch;
|
|
128487
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128488
|
+
}
|
|
128489
|
+
break;
|
|
128490
|
+
}
|
|
128491
|
+
case "bitbucket": {
|
|
128492
|
+
headSha = clean(env.BITBUCKET_COMMIT);
|
|
128493
|
+
if (clean(env.BITBUCKET_TAG)) {
|
|
128494
|
+
rawRef = clean(env.BITBUCKET_TAG);
|
|
128495
|
+
refKind = "tag";
|
|
128496
|
+
} else {
|
|
128497
|
+
headBranch = clean(env.BITBUCKET_BRANCH);
|
|
128498
|
+
rawRef = headBranch;
|
|
128499
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128500
|
+
isPrContext = Boolean(clean(env.BITBUCKET_PR_ID));
|
|
128501
|
+
}
|
|
128502
|
+
break;
|
|
128503
|
+
}
|
|
128504
|
+
case "azure-devops": {
|
|
128505
|
+
headSha = clean(env.BUILD_SOURCEVERSION);
|
|
128506
|
+
const prSource = clean(env.SYSTEM_PULLREQUEST_SOURCEBRANCH);
|
|
128507
|
+
if (prSource) {
|
|
128508
|
+
isPrContext = true;
|
|
128509
|
+
const parsed = stripRefPrefix(prSource);
|
|
128510
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128511
|
+
rawRef = prSource;
|
|
128512
|
+
refKind = parsed.kind;
|
|
128513
|
+
const forkFlag = clean(env.SYSTEM_PULLREQUEST_ISFORK);
|
|
128514
|
+
isForkPr = forkFlag?.toLowerCase() === "true";
|
|
128515
|
+
} else {
|
|
128516
|
+
rawRef = clean(env.BUILD_SOURCEBRANCH);
|
|
128517
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128518
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128519
|
+
refKind = parsed.kind;
|
|
128520
|
+
}
|
|
128521
|
+
break;
|
|
128522
|
+
}
|
|
128523
|
+
default: {
|
|
128524
|
+
refKind = "unknown";
|
|
128525
|
+
}
|
|
128526
|
+
}
|
|
128527
|
+
if (refKind === "branch" && headBranch && defaultBranch && headBranch === defaultBranch) {
|
|
128528
|
+
refKind = "default-branch";
|
|
128529
|
+
}
|
|
128530
|
+
return { provider, headBranch, rawRef, defaultBranch, refKind, isPrContext, isForkPr, headSha };
|
|
128531
|
+
}
|
|
128532
|
+
function parseChannelRules(input) {
|
|
128533
|
+
const raw = clean(input);
|
|
128534
|
+
const rules = [];
|
|
128535
|
+
for (const part of raw ? raw.split(",") : []) {
|
|
128536
|
+
const entry = part.trim();
|
|
128537
|
+
if (!entry) continue;
|
|
128538
|
+
const eq = entry.indexOf("=");
|
|
128539
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
128540
|
+
throw new ContractError(
|
|
128541
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128542
|
+
`channels entry "${entry}" must be <branch-or-glob>=<CODE>`
|
|
128543
|
+
);
|
|
128544
|
+
}
|
|
128545
|
+
const pattern = entry.slice(0, eq).trim();
|
|
128546
|
+
const code = entry.slice(eq + 1).trim().toUpperCase();
|
|
128547
|
+
if (!/^[A-Z][A-Z0-9_-]{0,15}$/.test(code)) {
|
|
128548
|
+
throw new ContractError(
|
|
128549
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128550
|
+
`channel code "${code}" must be 1-16 chars, A-Z 0-9 _ -, starting with a letter`
|
|
128551
|
+
);
|
|
128552
|
+
}
|
|
128553
|
+
rules.push({ pattern, code });
|
|
128554
|
+
}
|
|
128555
|
+
if (!rules.some((rule) => rule.pattern === "release/*")) {
|
|
128556
|
+
rules.push({ pattern: "release/*", code: "RC" });
|
|
128557
|
+
}
|
|
128558
|
+
return rules;
|
|
128559
|
+
}
|
|
128560
|
+
function matchChannel(branch, rules) {
|
|
128561
|
+
for (const rule of rules) {
|
|
128562
|
+
if (rule.pattern.endsWith("*")) {
|
|
128563
|
+
const prefix = rule.pattern.slice(0, -1);
|
|
128564
|
+
if (branch.startsWith(prefix)) return rule;
|
|
128565
|
+
} else if (branch === rule.pattern) {
|
|
128566
|
+
return rule;
|
|
128567
|
+
}
|
|
128568
|
+
}
|
|
128569
|
+
return void 0;
|
|
128570
|
+
}
|
|
128571
|
+
function resolveBranchDecision(options) {
|
|
128572
|
+
const { strategy, identity } = options;
|
|
128573
|
+
const channels = options.channels ?? [];
|
|
128574
|
+
if (strategy === "legacy") {
|
|
128575
|
+
return {
|
|
128576
|
+
tier: "legacy",
|
|
128577
|
+
strategy,
|
|
128578
|
+
identity,
|
|
128579
|
+
canonicalBranch: clean(options.canonicalBranch) ?? identity.defaultBranch,
|
|
128580
|
+
reason: "branch-strategy legacy: branch-blind pre-v2 behavior"
|
|
128581
|
+
};
|
|
128582
|
+
}
|
|
128583
|
+
const canonicalBranch = clean(options.canonicalBranch) ?? identity.defaultBranch;
|
|
128584
|
+
if (!canonicalBranch) {
|
|
128585
|
+
throw new ContractError(
|
|
128586
|
+
"CONTRACT_DEFAULT_BRANCH_UNRESOLVED",
|
|
128587
|
+
`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.`
|
|
128588
|
+
);
|
|
128589
|
+
}
|
|
128590
|
+
if (identity.refKind === "tag" || identity.refKind === "unknown" || !identity.headBranch) {
|
|
128591
|
+
return {
|
|
128592
|
+
tier: "gated",
|
|
128593
|
+
strategy,
|
|
128594
|
+
identity,
|
|
128595
|
+
canonicalBranch,
|
|
128596
|
+
reason: `ref kind ${identity.refKind}: never canonical/preview-eligible; no-op with annotation`
|
|
128597
|
+
};
|
|
128598
|
+
}
|
|
128599
|
+
if (identity.headBranch === canonicalBranch) {
|
|
128600
|
+
return {
|
|
128601
|
+
tier: "canonical",
|
|
128602
|
+
strategy,
|
|
128603
|
+
identity,
|
|
128604
|
+
canonicalBranch,
|
|
128605
|
+
reason: `head branch equals canonical branch ${canonicalBranch}`
|
|
128606
|
+
};
|
|
128607
|
+
}
|
|
128608
|
+
const channel = matchChannel(identity.headBranch, channels);
|
|
128609
|
+
if (channel) {
|
|
128610
|
+
return {
|
|
128611
|
+
tier: "channel",
|
|
128612
|
+
strategy,
|
|
128613
|
+
identity,
|
|
128614
|
+
canonicalBranch,
|
|
128615
|
+
channel,
|
|
128616
|
+
reason: `branch ${identity.headBranch} matches channel ${channel.pattern}=${channel.code}`
|
|
128617
|
+
};
|
|
128618
|
+
}
|
|
128619
|
+
if (strategy === "preview") {
|
|
128620
|
+
if (identity.isForkPr) {
|
|
128621
|
+
return {
|
|
128622
|
+
tier: "gated",
|
|
128623
|
+
strategy,
|
|
128624
|
+
identity,
|
|
128625
|
+
canonicalBranch,
|
|
128626
|
+
reason: "fork PR: preview-ineligible (same-repo gate), gated instead"
|
|
128627
|
+
};
|
|
128628
|
+
}
|
|
128629
|
+
return {
|
|
128630
|
+
tier: "preview",
|
|
128631
|
+
strategy,
|
|
128632
|
+
identity,
|
|
128633
|
+
canonicalBranch,
|
|
128634
|
+
reason: `branch ${identity.headBranch} under branch-strategy preview`
|
|
128635
|
+
};
|
|
128636
|
+
}
|
|
128637
|
+
return {
|
|
128638
|
+
tier: "gated",
|
|
128639
|
+
strategy,
|
|
128640
|
+
identity,
|
|
128641
|
+
canonicalBranch,
|
|
128642
|
+
reason: `branch ${identity.headBranch} under branch-strategy publish-gate`
|
|
128643
|
+
};
|
|
128644
|
+
}
|
|
128645
|
+
var BRANCH_DECISION_ENV = "POSTMAN_BRANCH_DECISION";
|
|
128646
|
+
function serializeBranchDecision(decision) {
|
|
128647
|
+
return JSON.stringify(decision);
|
|
128648
|
+
}
|
|
128649
|
+
function parseBranchDecision(raw) {
|
|
128650
|
+
const value = clean(raw);
|
|
128651
|
+
if (!value) return void 0;
|
|
128652
|
+
let parsed;
|
|
128653
|
+
try {
|
|
128654
|
+
parsed = JSON.parse(value);
|
|
128655
|
+
} catch {
|
|
128656
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION is not valid JSON");
|
|
128657
|
+
}
|
|
128658
|
+
const candidate = parsed;
|
|
128659
|
+
const tiers = ["canonical", "channel", "preview", "gated", "legacy"];
|
|
128660
|
+
if (!candidate || typeof candidate !== "object" || !tiers.includes(candidate.tier) || !candidate.identity) {
|
|
128661
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION does not carry a valid BranchDecision");
|
|
128662
|
+
}
|
|
128663
|
+
return candidate;
|
|
128664
|
+
}
|
|
128665
|
+
function resolveEffectiveBranchDecision(options, env = process.env) {
|
|
128666
|
+
const inherited = parseBranchDecision(env[BRANCH_DECISION_ENV]);
|
|
128667
|
+
if (inherited) return inherited;
|
|
128668
|
+
return resolveBranchDecision(options);
|
|
128669
|
+
}
|
|
128670
|
+
var PREVIEW_SLUG_MAX = 30;
|
|
128671
|
+
function buildBranchSlug(rawBranch) {
|
|
128672
|
+
const sanitized = rawBranch.replace(/^refs\/heads\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128673
|
+
const truncated = sanitized.slice(0, PREVIEW_SLUG_MAX);
|
|
128674
|
+
const lossy = truncated !== rawBranch.replace(/^refs\/heads\//, "");
|
|
128675
|
+
if (!lossy) {
|
|
128676
|
+
return { suffix: truncated, slug: truncated, lossy };
|
|
128677
|
+
}
|
|
128678
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(rawBranch).digest("hex").slice(0, 6);
|
|
128679
|
+
return { suffix: `${truncated}-${hash}`, slug: truncated, lossy };
|
|
128680
|
+
}
|
|
128681
|
+
function previewAssetName(baseName, rawBranch) {
|
|
128682
|
+
return `${baseName} @${buildBranchSlug(rawBranch).suffix}`;
|
|
128683
|
+
}
|
|
128684
|
+
function channelAssetName(baseName, code) {
|
|
128685
|
+
return `[${code}] ${baseName}`;
|
|
128686
|
+
}
|
|
128687
|
+
var MARKER_KEY = "x-pm-onboarding";
|
|
128688
|
+
function parseAssetMarker(description) {
|
|
128689
|
+
if (!description) return void 0;
|
|
128690
|
+
const index = description.indexOf(`${MARKER_KEY}:`);
|
|
128691
|
+
if (index === -1) return void 0;
|
|
128692
|
+
const jsonStart = description.indexOf("{", index);
|
|
128693
|
+
if (jsonStart === -1) return void 0;
|
|
128694
|
+
let depth = 0;
|
|
128695
|
+
for (let i = jsonStart; i < description.length; i += 1) {
|
|
128696
|
+
const ch = description[i];
|
|
128697
|
+
if (ch === "{") depth += 1;
|
|
128698
|
+
else if (ch === "}") {
|
|
128699
|
+
depth -= 1;
|
|
128700
|
+
if (depth === 0) {
|
|
128701
|
+
try {
|
|
128702
|
+
const parsed = JSON.parse(description.slice(jsonStart, i + 1));
|
|
128703
|
+
if (parsed && typeof parsed === "object" && parsed.repo && parsed.role) return parsed;
|
|
128704
|
+
} catch {
|
|
128705
|
+
return void 0;
|
|
128706
|
+
}
|
|
128707
|
+
return void 0;
|
|
128708
|
+
}
|
|
128709
|
+
}
|
|
128710
|
+
}
|
|
128711
|
+
return void 0;
|
|
128712
|
+
}
|
|
128713
|
+
|
|
128190
128714
|
// src/index.ts
|
|
128191
128715
|
function parseBooleanInput(value, defaultValue) {
|
|
128192
128716
|
const normalized = String(value || "").trim().toLowerCase();
|
|
@@ -128205,16 +128729,16 @@ function getInput2(name, env = process.env) {
|
|
|
128205
128729
|
const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
|
|
128206
128730
|
const hasNormalized = normalizedRaw !== void 0;
|
|
128207
128731
|
const hasRunner = runnerRaw !== void 0;
|
|
128732
|
+
const normalizedValue = normalizeInputValue(normalizedRaw);
|
|
128733
|
+
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128208
128734
|
if (hasNormalized && hasRunner) {
|
|
128209
|
-
|
|
128210
|
-
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128211
|
-
if (normalizedValue !== runnerValue) {
|
|
128735
|
+
if (normalizedValue && runnerValue && normalizedValue !== runnerValue) {
|
|
128212
128736
|
throw new Error(
|
|
128213
128737
|
`Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
|
|
128214
128738
|
);
|
|
128215
128739
|
}
|
|
128216
128740
|
}
|
|
128217
|
-
return
|
|
128741
|
+
return normalizedValue || runnerValue;
|
|
128218
128742
|
}
|
|
128219
128743
|
function hasInput(name, env = process.env) {
|
|
128220
128744
|
const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
|
|
@@ -128276,6 +128800,17 @@ function parseCredentialPreflight(value) {
|
|
|
128276
128800
|
`Unsupported credential-preflight "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128277
128801
|
);
|
|
128278
128802
|
}
|
|
128803
|
+
function parseBranchStrategy(value) {
|
|
128804
|
+
const definition = postmanRepoSyncActionContract.inputs["branch-strategy"];
|
|
128805
|
+
const allowed = definition.allowedValues ?? [];
|
|
128806
|
+
const normalized = String(value || "").trim() || (definition.default ?? "legacy");
|
|
128807
|
+
if (allowed.includes(normalized)) {
|
|
128808
|
+
return normalized;
|
|
128809
|
+
}
|
|
128810
|
+
throw new Error(
|
|
128811
|
+
`Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128812
|
+
);
|
|
128813
|
+
}
|
|
128279
128814
|
function normalizeReleaseLabel(value) {
|
|
128280
128815
|
const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128281
128816
|
return cleaned;
|
|
@@ -128320,6 +128855,7 @@ function resolveInputs(env = process.env) {
|
|
|
128320
128855
|
smokeCollectionId: getInput2("smoke-collection-id", env),
|
|
128321
128856
|
contractCollectionId: getInput2("contract-collection-id", env),
|
|
128322
128857
|
specId: getInput2("spec-id", env),
|
|
128858
|
+
specContentChanged: parseBooleanInput(getInput2("spec-content-changed", env), true),
|
|
128323
128859
|
specPath: getInput2("spec-path", env),
|
|
128324
128860
|
collectionSyncMode: normalizeCollectionSyncMode(getInput2("collection-sync-mode", env) || "refresh"),
|
|
128325
128861
|
specSyncMode: normalizeSpecSyncMode(getInput2("spec-sync-mode", env) || "update"),
|
|
@@ -128342,6 +128878,10 @@ function resolveInputs(env = process.env) {
|
|
|
128342
128878
|
postmanApiKey: getInput2("postman-api-key", env),
|
|
128343
128879
|
postmanAccessToken: getInput2("postman-access-token", env),
|
|
128344
128880
|
credentialPreflight: parseCredentialPreflight(getInput2("credential-preflight", env)),
|
|
128881
|
+
branchStrategy: parseBranchStrategy(getInput2("branch-strategy", env)),
|
|
128882
|
+
canonicalBranch: getInput2("canonical-branch", env) || void 0,
|
|
128883
|
+
channels: getInput2("channels", env) || void 0,
|
|
128884
|
+
previewTtlDays: Math.max(1, Number.parseInt(getInput2("preview-ttl", env) || "30", 10) || 30),
|
|
128345
128885
|
adoToken: getInput2("ado-token", env) || normalizeInputValue(env.SYSTEM_ACCESSTOKEN),
|
|
128346
128886
|
githubToken: getInput2("github-token", env),
|
|
128347
128887
|
ghFallbackToken: getInput2("gh-fallback-token", env),
|
|
@@ -128368,6 +128908,40 @@ function resolveInputs(env = process.env) {
|
|
|
128368
128908
|
postmanIapubBase: endpointProfile.iapubBaseUrl
|
|
128369
128909
|
};
|
|
128370
128910
|
}
|
|
128911
|
+
function buildBranchAssetMarker(decision, inputs, now = /* @__PURE__ */ new Date()) {
|
|
128912
|
+
if (decision.tier !== "preview" && decision.tier !== "channel") {
|
|
128913
|
+
return void 0;
|
|
128914
|
+
}
|
|
128915
|
+
const rawBranch = decision.identity.headBranch;
|
|
128916
|
+
const repo = inputs.repoUrl || inputs.repository;
|
|
128917
|
+
if (!rawBranch || !repo) {
|
|
128918
|
+
return void 0;
|
|
128919
|
+
}
|
|
128920
|
+
const ttlMs = inputs.previewTtlDays * 24 * 60 * 60 * 1e3;
|
|
128921
|
+
return {
|
|
128922
|
+
repo,
|
|
128923
|
+
rawBranch,
|
|
128924
|
+
sanitizedBranch: buildBranchSlug(rawBranch).suffix,
|
|
128925
|
+
role: decision.tier,
|
|
128926
|
+
headSha: decision.identity.headSha,
|
|
128927
|
+
createdAt: now.toISOString(),
|
|
128928
|
+
lastSyncedAt: now.toISOString(),
|
|
128929
|
+
...decision.tier === "preview" ? { expiresAt: new Date(now.getTime() + ttlMs).toISOString() } : {}
|
|
128930
|
+
};
|
|
128931
|
+
}
|
|
128932
|
+
function assertBranchAssetIds(inputs, decision, branchOwnedIds = process.env.POSTMAN_BRANCH_ASSET_IDS === "owned") {
|
|
128933
|
+
if (decision.tier === "legacy" || decision.tier === "canonical" || branchOwnedIds) return;
|
|
128934
|
+
const provided = [
|
|
128935
|
+
["baseline-collection-id", inputs.baselineCollectionId],
|
|
128936
|
+
["smoke-collection-id", inputs.smokeCollectionId],
|
|
128937
|
+
["contract-collection-id", inputs.contractCollectionId]
|
|
128938
|
+
].filter(([, value]) => Boolean(value));
|
|
128939
|
+
if (provided.length > 0) {
|
|
128940
|
+
throw new Error(
|
|
128941
|
+
`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.`
|
|
128942
|
+
);
|
|
128943
|
+
}
|
|
128944
|
+
}
|
|
128371
128945
|
function buildEnvironmentValues(envName, baseUrl) {
|
|
128372
128946
|
return [
|
|
128373
128947
|
{ key: "baseUrl", value: baseUrl, type: "default" },
|
|
@@ -128380,12 +128954,48 @@ function buildEnvironmentValues(envName, baseUrl) {
|
|
|
128380
128954
|
];
|
|
128381
128955
|
}
|
|
128382
128956
|
var LEGACY_BASELINE_COLLECTION_PREFIX = "[Baseline]";
|
|
128957
|
+
var RESOURCES_STATE_VERSION = 2;
|
|
128958
|
+
var SUPPORTED_STATE_VERSIONS = /* @__PURE__ */ new Set([1, RESOURCES_STATE_VERSION]);
|
|
128959
|
+
var StateUnreadableError = class extends Error {
|
|
128960
|
+
code = "CONTRACT_STATE_UNREADABLE";
|
|
128961
|
+
constructor(message) {
|
|
128962
|
+
super(`CONTRACT_STATE_UNREADABLE: ${message}`);
|
|
128963
|
+
this.name = "StateUnreadableError";
|
|
128964
|
+
}
|
|
128965
|
+
};
|
|
128383
128966
|
function readResourcesState() {
|
|
128967
|
+
let raw;
|
|
128384
128968
|
try {
|
|
128385
|
-
|
|
128969
|
+
raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
|
|
128386
128970
|
} catch {
|
|
128387
128971
|
return null;
|
|
128388
128972
|
}
|
|
128973
|
+
let parsed;
|
|
128974
|
+
try {
|
|
128975
|
+
parsed = load(raw);
|
|
128976
|
+
} catch (error2) {
|
|
128977
|
+
throw new StateUnreadableError(
|
|
128978
|
+
`.postman/resources.yaml exists but is not parseable YAML (${error2 instanceof Error ? error2.message : String(error2)}). Fix or delete the file; refusing to treat tracked state as absent.`
|
|
128979
|
+
);
|
|
128980
|
+
}
|
|
128981
|
+
if (parsed === null || parsed === void 0) {
|
|
128982
|
+
return null;
|
|
128983
|
+
}
|
|
128984
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
128985
|
+
throw new StateUnreadableError(
|
|
128986
|
+
".postman/resources.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to treat tracked state as absent."
|
|
128987
|
+
);
|
|
128988
|
+
}
|
|
128989
|
+
const state = parsed;
|
|
128990
|
+
if (state.version !== void 0 && !SUPPORTED_STATE_VERSIONS.has(Number(state.version))) {
|
|
128991
|
+
throw new StateUnreadableError(
|
|
128992
|
+
`.postman/resources.yaml declares unsupported state version ${String(state.version)} (supported: 1, ${RESOURCES_STATE_VERSION}). Upgrade the action or fix the file.`
|
|
128993
|
+
);
|
|
128994
|
+
}
|
|
128995
|
+
if (state.canonical && !state.cloudResources) {
|
|
128996
|
+
state.cloudResources = { ...state.canonical };
|
|
128997
|
+
}
|
|
128998
|
+
return state;
|
|
128389
128999
|
}
|
|
128390
129000
|
function findCloudResourceId(map, matcher) {
|
|
128391
129001
|
if (!map) {
|
|
@@ -128420,7 +129030,7 @@ function isOpenApiSpecFile(filePath) {
|
|
|
128420
129030
|
return false;
|
|
128421
129031
|
}
|
|
128422
129032
|
try {
|
|
128423
|
-
const raw = (0,
|
|
129033
|
+
const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
|
|
128424
129034
|
const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
|
|
128425
129035
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
128426
129036
|
return false;
|
|
@@ -128449,7 +129059,7 @@ function scanLocalSpecReferences(baseDir = ".") {
|
|
|
128449
129059
|
const found = /* @__PURE__ */ new Set();
|
|
128450
129060
|
const refs = [];
|
|
128451
129061
|
const visit2 = (currentDir) => {
|
|
128452
|
-
for (const entry of (0,
|
|
129062
|
+
for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
|
|
128453
129063
|
if (ignoredDirs.has(entry.name)) {
|
|
128454
129064
|
continue;
|
|
128455
129065
|
}
|
|
@@ -128479,7 +129089,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
|
|
|
128479
129089
|
const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
|
|
128480
129090
|
if (normalizedExplicitPath) {
|
|
128481
129091
|
const explicitFullPath = path8.resolve(normalizedExplicitPath);
|
|
128482
|
-
if ((0,
|
|
129092
|
+
if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
|
|
128483
129093
|
return {
|
|
128484
129094
|
repoRelativePath: normalizedExplicitPath,
|
|
128485
129095
|
configRelativePath: normalizeToPosix(path8.join("..", normalizedExplicitPath))
|
|
@@ -128506,7 +129116,11 @@ function createOutputs(inputs) {
|
|
|
128506
129116
|
"mock-url": "",
|
|
128507
129117
|
"monitor-id": "",
|
|
128508
129118
|
"repo-sync-summary-json": "{}",
|
|
128509
|
-
"commit-sha": ""
|
|
129119
|
+
"commit-sha": "",
|
|
129120
|
+
"sync-status": "",
|
|
129121
|
+
"branch-decision": "",
|
|
129122
|
+
"spec-version-tag": "",
|
|
129123
|
+
"spec-version-url": ""
|
|
128510
129124
|
};
|
|
128511
129125
|
}
|
|
128512
129126
|
function readActionInputs(actionCore) {
|
|
@@ -128551,6 +129165,10 @@ function readActionInputs(actionCore) {
|
|
|
128551
129165
|
INPUT_POSTMAN_API_KEY: postmanApiKey,
|
|
128552
129166
|
INPUT_POSTMAN_ACCESS_TOKEN: postmanAccessToken,
|
|
128553
129167
|
INPUT_CREDENTIAL_PREFLIGHT: readInput(actionCore, "credential-preflight") || "warn",
|
|
129168
|
+
INPUT_BRANCH_STRATEGY: readInput(actionCore, "branch-strategy"),
|
|
129169
|
+
INPUT_CANONICAL_BRANCH: readInput(actionCore, "canonical-branch"),
|
|
129170
|
+
INPUT_CHANNELS: readInput(actionCore, "channels"),
|
|
129171
|
+
INPUT_PREVIEW_TTL: readInput(actionCore, "preview-ttl"),
|
|
128554
129172
|
INPUT_ADO_TOKEN: adoToken,
|
|
128555
129173
|
INPUT_GITHUB_TOKEN: githubToken,
|
|
128556
129174
|
INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
|
|
@@ -128665,7 +129283,7 @@ async function persistSslSecrets(inputs, actionCore, actionExec, repository, env
|
|
|
128665
129283
|
);
|
|
128666
129284
|
}
|
|
128667
129285
|
}
|
|
128668
|
-
async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
129286
|
+
async function upsertEnvironments(inputs, dependencies, resourcesState, assetMarker) {
|
|
128669
129287
|
const envUids = {
|
|
128670
129288
|
...getEnvironmentUidsFromResources(resourcesState),
|
|
128671
129289
|
...inputs.environmentUids
|
|
@@ -128675,7 +129293,6 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128675
129293
|
}
|
|
128676
129294
|
for (const envName of inputs.environments) {
|
|
128677
129295
|
const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
|
|
128678
|
-
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
128679
129296
|
const displayName = `${inputs.projectName} - ${envName}`;
|
|
128680
129297
|
let existingUid = String(envUids[envName] || "").trim();
|
|
128681
129298
|
if (!existingUid) {
|
|
@@ -128691,10 +129308,27 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128691
129308
|
}
|
|
128692
129309
|
}
|
|
128693
129310
|
if (existingUid) {
|
|
128694
|
-
|
|
129311
|
+
let marker = assetMarker;
|
|
129312
|
+
if (marker) {
|
|
129313
|
+
try {
|
|
129314
|
+
const existing = await dependencies.postman.getEnvironment(existingUid);
|
|
129315
|
+
const values3 = existing.data?.values ?? existing.values ?? [];
|
|
129316
|
+
const prior = values3.find((value) => value.key === "x-pm-onboarding")?.value;
|
|
129317
|
+
const priorMarker = parseAssetMarker(prior ? `x-pm-onboarding: ${prior}` : void 0);
|
|
129318
|
+
if (priorMarker?.repo === marker.repo && priorMarker.rawBranch === marker.rawBranch) {
|
|
129319
|
+
marker = { ...marker, createdAt: priorMarker.createdAt };
|
|
129320
|
+
}
|
|
129321
|
+
} catch {
|
|
129322
|
+
}
|
|
129323
|
+
}
|
|
129324
|
+
const values2 = buildEnvironmentValues(envName, runtimeUrl);
|
|
129325
|
+
if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
|
|
129326
|
+
await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
|
|
128695
129327
|
envUids[envName] = existingUid;
|
|
128696
129328
|
continue;
|
|
128697
129329
|
}
|
|
129330
|
+
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
129331
|
+
if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
|
|
128698
129332
|
envUids[envName] = await dependencies.postman.createEnvironment(
|
|
128699
129333
|
inputs.workspaceId,
|
|
128700
129334
|
displayName,
|
|
@@ -128704,7 +129338,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128704
129338
|
return envUids;
|
|
128705
129339
|
}
|
|
128706
129340
|
function ensureDir(path9) {
|
|
128707
|
-
(0,
|
|
129341
|
+
(0, import_node_fs4.mkdirSync)(path9, { recursive: true });
|
|
128708
129342
|
}
|
|
128709
129343
|
function getCollectionDirectoryName(kind, projectName) {
|
|
128710
129344
|
if (kind === "Baseline") {
|
|
@@ -128741,7 +129375,7 @@ function stripVolatileFields(obj) {
|
|
|
128741
129375
|
}
|
|
128742
129376
|
function writeJsonFile(path9, content, normalize3 = false) {
|
|
128743
129377
|
const data = normalize3 ? stripVolatileFields(content) : content;
|
|
128744
|
-
(0,
|
|
129378
|
+
(0, import_node_fs4.writeFileSync)(path9, JSON.stringify(data, null, 2));
|
|
128745
129379
|
}
|
|
128746
129380
|
function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
|
|
128747
129381
|
if (specSyncMode !== "version") {
|
|
@@ -128765,31 +129399,30 @@ function resolveDurableWorkspaceId(options) {
|
|
|
128765
129399
|
}
|
|
128766
129400
|
return prior === candidate ? prior : void 0;
|
|
128767
129401
|
}
|
|
128768
|
-
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs) {
|
|
128769
|
-
const manifest = {};
|
|
129402
|
+
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs, priorState) {
|
|
129403
|
+
const manifest = { ...priorState ?? {} };
|
|
129404
|
+
delete manifest.version;
|
|
129405
|
+
delete manifest.workspace;
|
|
129406
|
+
delete manifest.localResources;
|
|
129407
|
+
delete manifest.cloudResources;
|
|
129408
|
+
delete manifest.canonical;
|
|
129409
|
+
manifest.version = RESOURCES_STATE_VERSION;
|
|
128770
129410
|
if (workspaceId) {
|
|
128771
129411
|
manifest.workspace = { id: workspaceId };
|
|
128772
129412
|
}
|
|
128773
|
-
const localResources = {};
|
|
128774
129413
|
const cloudResources = {};
|
|
128775
129414
|
const collectionKeys = Object.keys(collectionMap);
|
|
128776
129415
|
if (collectionKeys.length > 0) {
|
|
128777
|
-
localResources.collections = collectionKeys;
|
|
128778
129416
|
cloudResources.collections = collectionMap;
|
|
128779
129417
|
}
|
|
128780
129418
|
const envEntries = Object.entries(envMap);
|
|
128781
129419
|
if (envEntries.length > 0) {
|
|
128782
|
-
localResources.environments = envEntries.map(
|
|
128783
|
-
([envName]) => `../${artifactDir}/environments/${envName}.postman_environment.json`
|
|
128784
|
-
);
|
|
128785
129420
|
cloudResources.environments = {};
|
|
128786
129421
|
for (const [envName, envUid] of envEntries) {
|
|
128787
129422
|
cloudResources.environments[`../${artifactDir}/environments/${envName}.postman_environment.json`] = envUid;
|
|
128788
129423
|
}
|
|
128789
129424
|
}
|
|
128790
|
-
|
|
128791
|
-
localResources.specs = localSpecRefs;
|
|
128792
|
-
}
|
|
129425
|
+
void localSpecRefs;
|
|
128793
129426
|
const specs = { ...existingSpecs || {} };
|
|
128794
129427
|
if (mappedSpecRef && specId) {
|
|
128795
129428
|
specs[mappedSpecRef] = specId;
|
|
@@ -128797,11 +129430,8 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
|
|
|
128797
129430
|
if (Object.keys(specs).length > 0) {
|
|
128798
129431
|
cloudResources.specs = specs;
|
|
128799
129432
|
}
|
|
128800
|
-
if (Object.keys(localResources).length > 0) {
|
|
128801
|
-
manifest.localResources = localResources;
|
|
128802
|
-
}
|
|
128803
129433
|
if (Object.keys(cloudResources).length > 0) {
|
|
128804
|
-
manifest.
|
|
129434
|
+
manifest.canonical = cloudResources;
|
|
128805
129435
|
}
|
|
128806
129436
|
return dump(manifest, {
|
|
128807
129437
|
lineWidth: -1,
|
|
@@ -128842,21 +129472,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
|
|
|
128842
129472
|
if (!rawPath || hasControlCharacter2(originalPath) || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
|
|
128843
129473
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128844
129474
|
}
|
|
128845
|
-
const base = (0,
|
|
129475
|
+
const base = (0, import_node_fs4.realpathSync)(process.cwd());
|
|
128846
129476
|
const resolved = path8.resolve(base, rawPath);
|
|
128847
129477
|
const relative4 = path8.relative(base, resolved);
|
|
128848
129478
|
if (relative4.startsWith("..") || path8.isAbsolute(relative4)) {
|
|
128849
129479
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128850
129480
|
}
|
|
128851
129481
|
let existingPath = resolved;
|
|
128852
|
-
while (!(0,
|
|
129482
|
+
while (!(0, import_node_fs4.existsSync)(existingPath)) {
|
|
128853
129483
|
const parent = path8.dirname(existingPath);
|
|
128854
129484
|
if (parent === existingPath) {
|
|
128855
129485
|
break;
|
|
128856
129486
|
}
|
|
128857
129487
|
existingPath = parent;
|
|
128858
129488
|
}
|
|
128859
|
-
const realExistingPath = (0,
|
|
129489
|
+
const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
|
|
128860
129490
|
const realRelative = path8.relative(base, realExistingPath);
|
|
128861
129491
|
if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
|
|
128862
129492
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
@@ -128884,8 +129514,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128884
129514
|
ensureDir(specsDir);
|
|
128885
129515
|
ensureDir(".postman");
|
|
128886
129516
|
const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
|
|
128887
|
-
if (!(0,
|
|
128888
|
-
(0,
|
|
129517
|
+
if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
|
|
129518
|
+
(0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
|
|
128889
129519
|
}
|
|
128890
129520
|
if (inputs.generateCiWorkflow) {
|
|
128891
129521
|
const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
|
|
@@ -128932,7 +129562,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128932
129562
|
workspaceLinkEnabled: inputs.workspaceLinkEnabled,
|
|
128933
129563
|
workspaceLinkStatus: options.workspaceLinkStatus
|
|
128934
129564
|
});
|
|
128935
|
-
(0,
|
|
129565
|
+
(0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
128936
129566
|
durableWorkspaceId,
|
|
128937
129567
|
manifestCollections,
|
|
128938
129568
|
envUids,
|
|
@@ -128940,10 +129570,11 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128940
129570
|
discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
128941
129571
|
mappedSpecCloudKey,
|
|
128942
129572
|
inputs.specId || void 0,
|
|
128943
|
-
options.existingSpecs
|
|
129573
|
+
options.existingSpecs,
|
|
129574
|
+
options.priorState
|
|
128944
129575
|
));
|
|
128945
129576
|
if (mappedSpec && Object.keys(manifestCollections).length > 0) {
|
|
128946
|
-
(0,
|
|
129577
|
+
(0, import_node_fs4.writeFileSync)(
|
|
128947
129578
|
".postman/workflows.yaml",
|
|
128948
129579
|
buildSpecCollectionWorkflowManifest(
|
|
128949
129580
|
mappedSpec.configRelativePath,
|
|
@@ -128988,19 +129619,29 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
|
|
|
128988
129619
|
const dir = parts.slice(0, -1).join("/");
|
|
128989
129620
|
ensureDir(dir);
|
|
128990
129621
|
}
|
|
128991
|
-
(0,
|
|
129622
|
+
(0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
|
|
129623
|
+
if (inputs.provider === "github" || inputs.provider === "unknown") {
|
|
129624
|
+
const gcPath = ".github/workflows/postman-preview-gc.yml";
|
|
129625
|
+
if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
|
|
129626
|
+
ensureDir(".github/workflows");
|
|
129627
|
+
(0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
|
|
129628
|
+
}
|
|
129629
|
+
}
|
|
128992
129630
|
}
|
|
128993
129631
|
if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
|
|
128994
129632
|
return { commitSha: "", resolvedCurrentRef: "", pushed: false };
|
|
128995
129633
|
}
|
|
128996
129634
|
const provisionPath = ".github/workflows/provision.yml";
|
|
128997
|
-
const provisionExists = inputs.provider === "github" && (0,
|
|
129635
|
+
const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
|
|
129636
|
+
const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
|
|
129637
|
+
const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
|
|
128998
129638
|
const stagePaths = [
|
|
128999
129639
|
inputs.artifactDir,
|
|
129000
129640
|
".postman",
|
|
129001
129641
|
inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
|
|
129642
|
+
gcExists ? gcWorkflowPath : null,
|
|
129002
129643
|
provisionExists ? provisionPath : null
|
|
129003
|
-
].filter((entry) => typeof entry === "string" && ((0,
|
|
129644
|
+
].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
|
|
129004
129645
|
if (stagePaths.length === 0) {
|
|
129005
129646
|
dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
|
|
129006
129647
|
return {
|
|
@@ -129048,6 +129689,31 @@ async function runRepoSync(inputs, dependencies) {
|
|
|
129048
129689
|
}
|
|
129049
129690
|
}
|
|
129050
129691
|
async function runRepoSyncInner(inputs, dependencies) {
|
|
129692
|
+
const branchDecision = decideBranchTier(inputs);
|
|
129693
|
+
assertBranchAssetIds(inputs, branchDecision);
|
|
129694
|
+
const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
|
|
129695
|
+
if (!isCanonicalWriter) {
|
|
129696
|
+
if (branchDecision.tier === "preview" && branchDecision.identity.headBranch) {
|
|
129697
|
+
inputs = {
|
|
129698
|
+
...inputs,
|
|
129699
|
+
projectName: previewAssetName(inputs.projectName, branchDecision.identity.headBranch),
|
|
129700
|
+
workspaceLinkEnabled: false,
|
|
129701
|
+
environmentSyncEnabled: false,
|
|
129702
|
+
repoWriteMode: "none"
|
|
129703
|
+
};
|
|
129704
|
+
} else if (branchDecision.tier === "channel" && branchDecision.channel) {
|
|
129705
|
+
inputs = {
|
|
129706
|
+
...inputs,
|
|
129707
|
+
projectName: channelAssetName(inputs.projectName, branchDecision.channel.code),
|
|
129708
|
+
workspaceLinkEnabled: false,
|
|
129709
|
+
environmentSyncEnabled: false,
|
|
129710
|
+
repoWriteMode: "none"
|
|
129711
|
+
};
|
|
129712
|
+
}
|
|
129713
|
+
dependencies.core.info(
|
|
129714
|
+
`branch-aware sync: ${branchDecision.tier} run \u2014 branch-scoped asset set "${inputs.projectName}", no workspace repo-link mutation, no state write-back`
|
|
129715
|
+
);
|
|
129716
|
+
}
|
|
129051
129717
|
const outputs = createOutputs(inputs);
|
|
129052
129718
|
const versionRequested = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
|
|
129053
129719
|
const releaseLabel = deriveReleaseLabel(inputs);
|
|
@@ -129055,7 +129721,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129055
129721
|
throw new Error("release-label is required when collection-sync-mode or spec-sync-mode is version");
|
|
129056
129722
|
}
|
|
129057
129723
|
const assetProjectName = createAssetProjectName(inputs, releaseLabel);
|
|
129058
|
-
const
|
|
129724
|
+
const trackedState = readResourcesState();
|
|
129725
|
+
const resourcesState = isCanonicalWriter ? trackedState : trackedState?.workspace ? { workspace: trackedState.workspace } : null;
|
|
129059
129726
|
if (resourcesState) {
|
|
129060
129727
|
if (!inputs.workspaceId && resourcesState.workspace?.id) {
|
|
129061
129728
|
inputs.workspaceId = resourcesState.workspace.id;
|
|
@@ -129081,7 +129748,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129081
129748
|
}
|
|
129082
129749
|
}
|
|
129083
129750
|
}
|
|
129084
|
-
const
|
|
129751
|
+
const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
|
|
129752
|
+
const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
|
|
129085
129753
|
outputs["environment-uids-json"] = JSON.stringify(envUids);
|
|
129086
129754
|
if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
|
|
129087
129755
|
const associations = Object.entries(envUids).map(([envName, envUid]) => ({
|
|
@@ -129137,11 +129805,23 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129137
129805
|
}
|
|
129138
129806
|
}
|
|
129139
129807
|
if (!resolvedMockUrl) {
|
|
129140
|
-
const mock = await
|
|
129141
|
-
|
|
129142
|
-
|
|
129143
|
-
|
|
129144
|
-
|
|
129808
|
+
const mock = await retry(
|
|
129809
|
+
() => dependencies.postman.createMock(
|
|
129810
|
+
inputs.workspaceId,
|
|
129811
|
+
mockName,
|
|
129812
|
+
inputs.baselineCollectionId,
|
|
129813
|
+
mockEnvUid
|
|
129814
|
+
),
|
|
129815
|
+
{
|
|
129816
|
+
maxAttempts: 3,
|
|
129817
|
+
delayMs: 2e3,
|
|
129818
|
+
backoffMultiplier: 2,
|
|
129819
|
+
onRetry: ({ attempt, error: error2 }) => {
|
|
129820
|
+
dependencies.core.warning(
|
|
129821
|
+
`Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
|
|
129822
|
+
);
|
|
129823
|
+
}
|
|
129824
|
+
}
|
|
129145
129825
|
);
|
|
129146
129826
|
resolvedMockUrl = mock.url;
|
|
129147
129827
|
dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
|
|
@@ -129210,16 +129890,19 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129210
129890
|
);
|
|
129211
129891
|
} catch (error2) {
|
|
129212
129892
|
outputs["workspace-link-status"] = "failed";
|
|
129213
|
-
|
|
129214
|
-
|
|
129215
|
-
|
|
129893
|
+
const message = `Workspace link failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
129894
|
+
if (branchDecision.tier === "canonical") {
|
|
129895
|
+
throw new Error(message, { cause: error2 });
|
|
129896
|
+
}
|
|
129897
|
+
dependencies.core.warning(message);
|
|
129216
129898
|
}
|
|
129217
129899
|
}
|
|
129218
129900
|
await exportArtifacts(inputs, dependencies, envUids, assetProjectName, {
|
|
129219
129901
|
workspaceLinkStatus: outputs["workspace-link-status"],
|
|
129220
129902
|
priorWorkspaceId: resourcesState?.workspace?.id,
|
|
129221
129903
|
existingSpecs: resourcesState?.cloudResources?.specs,
|
|
129222
|
-
releaseLabel
|
|
129904
|
+
releaseLabel,
|
|
129905
|
+
priorState: resourcesState
|
|
129223
129906
|
});
|
|
129224
129907
|
const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
|
|
129225
129908
|
outputs["commit-sha"] = commit.commitSha;
|
|
@@ -129227,6 +129910,38 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129227
129910
|
outputs["resolved-current-ref"] = commit.resolvedCurrentRef;
|
|
129228
129911
|
}
|
|
129229
129912
|
outputs["repo-sync-summary-json"] = createRepoSummary(outputs, envUids, commit.pushed);
|
|
129913
|
+
if (branchDecision.tier === "canonical" && inputs.specId && inputs.specContentChanged !== false && dependencies.postman.tagSpecVersion) {
|
|
129914
|
+
const shortSha = (branchDecision.identity.headSha ?? "").slice(0, 7);
|
|
129915
|
+
const tagName = inputs.releaseLabel ? `${inputs.releaseLabel}${shortSha ? ` (${shortSha})` : ""}` : shortSha || `sync-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
129916
|
+
try {
|
|
129917
|
+
const tag = await dependencies.postman.tagSpecVersion(inputs.specId, tagName);
|
|
129918
|
+
outputs["spec-version-tag"] = tag.name || tagName;
|
|
129919
|
+
if (tag.id) {
|
|
129920
|
+
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)}`;
|
|
129921
|
+
}
|
|
129922
|
+
dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
|
|
129923
|
+
} catch (error2) {
|
|
129924
|
+
const status = error2.status;
|
|
129925
|
+
if (status === 409) {
|
|
129926
|
+
const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
|
|
129927
|
+
const latest = tags[0];
|
|
129928
|
+
if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
|
|
129929
|
+
outputs["spec-version-tag"] = latest.name;
|
|
129930
|
+
outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
|
|
129931
|
+
dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
|
|
129932
|
+
} else {
|
|
129933
|
+
dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
|
|
129934
|
+
}
|
|
129935
|
+
} else {
|
|
129936
|
+
dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
129937
|
+
}
|
|
129938
|
+
}
|
|
129939
|
+
}
|
|
129940
|
+
if (inputs.branchStrategy !== "legacy" || process.env[BRANCH_DECISION_ENV]) {
|
|
129941
|
+
const decision = decideBranchTier(inputs);
|
|
129942
|
+
outputs["sync-status"] = "synced";
|
|
129943
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
129944
|
+
}
|
|
129230
129945
|
for (const [name, value] of Object.entries(outputs)) {
|
|
129231
129946
|
dependencies.core.setOutput(name, value);
|
|
129232
129947
|
}
|
|
@@ -129416,7 +130131,19 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129416
130131
|
listMonitors: gatewayAssets.listMonitors.bind(gatewayAssets),
|
|
129417
130132
|
monitorExists: gatewayAssets.monitorExists.bind(gatewayAssets),
|
|
129418
130133
|
findMonitorByCollection: gatewayAssets.findMonitorByCollection.bind(gatewayAssets),
|
|
129419
|
-
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets)
|
|
130134
|
+
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets),
|
|
130135
|
+
listEnvironments: gatewayAssets.listEnvironments.bind(gatewayAssets),
|
|
130136
|
+
// GC path (preview/channel retention machine — lib/repo/preview-gc.ts).
|
|
130137
|
+
deleteEnvironment: gatewayAssets.deleteEnvironment.bind(gatewayAssets),
|
|
130138
|
+
deleteMock: gatewayAssets.deleteMock.bind(gatewayAssets),
|
|
130139
|
+
deleteMonitor: gatewayAssets.deleteMonitor.bind(gatewayAssets),
|
|
130140
|
+
deleteCollection: gatewayAssets.deleteCollection.bind(gatewayAssets),
|
|
130141
|
+
listSpecifications: gatewayAssets.listSpecifications.bind(gatewayAssets),
|
|
130142
|
+
getSpecContent: gatewayAssets.getSpecContent.bind(gatewayAssets),
|
|
130143
|
+
listSpecCollections: gatewayAssets.listSpecCollections.bind(gatewayAssets),
|
|
130144
|
+
deleteSpec: gatewayAssets.deleteSpec.bind(gatewayAssets),
|
|
130145
|
+
tagSpecVersion: gatewayAssets.tagSpecVersion.bind(gatewayAssets),
|
|
130146
|
+
listSpecVersionTags: gatewayAssets.listSpecVersionTags.bind(gatewayAssets)
|
|
129420
130147
|
};
|
|
129421
130148
|
const repoMutation = repository && (inputs.repoWriteMode === "commit-only" || inputs.repoWriteMode === "commit-and-push") ? new RepoMutationService({
|
|
129422
130149
|
provider: inputs.provider,
|
|
@@ -129453,8 +130180,43 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129453
130180
|
repoMutation
|
|
129454
130181
|
};
|
|
129455
130182
|
}
|
|
130183
|
+
function decideBranchTier(inputs, env = process.env) {
|
|
130184
|
+
return resolveEffectiveBranchDecision(
|
|
130185
|
+
{
|
|
130186
|
+
strategy: inputs.branchStrategy,
|
|
130187
|
+
identity: resolveBranchIdentity(env, { defaultBranch: inputs.canonicalBranch }),
|
|
130188
|
+
canonicalBranch: inputs.canonicalBranch,
|
|
130189
|
+
channels: parseChannelRules(inputs.channels)
|
|
130190
|
+
},
|
|
130191
|
+
env
|
|
130192
|
+
);
|
|
130193
|
+
}
|
|
130194
|
+
function runGatedSkip(inputs, decision, actionCore) {
|
|
130195
|
+
actionCore.info(
|
|
130196
|
+
`branch-aware sync: gated run (${decision.reason}) \u2014 repo-sync skipped, zero workspace writes`
|
|
130197
|
+
);
|
|
130198
|
+
const outputs = createOutputs(inputs);
|
|
130199
|
+
outputs["sync-status"] = "skipped-branch-gate";
|
|
130200
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
130201
|
+
outputs["repo-sync-summary-json"] = JSON.stringify({
|
|
130202
|
+
status: "skipped-branch-gate",
|
|
130203
|
+
reason: decision.reason
|
|
130204
|
+
});
|
|
130205
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
130206
|
+
actionCore.setOutput(name, value);
|
|
130207
|
+
}
|
|
130208
|
+
return outputs;
|
|
130209
|
+
}
|
|
129456
130210
|
async function runAction(actionCore = core_exports, actionExec = exec_exports) {
|
|
129457
130211
|
const inputs = readActionInputs(actionCore);
|
|
130212
|
+
const branchDecision = decideBranchTier(inputs);
|
|
130213
|
+
if (branchDecision.tier === "gated") {
|
|
130214
|
+
return runGatedSkip(inputs, branchDecision, actionCore);
|
|
130215
|
+
}
|
|
130216
|
+
if (branchDecision.tier !== "legacy") {
|
|
130217
|
+
actionCore.info(`branch-aware sync: tier=${branchDecision.tier} (${branchDecision.reason})`);
|
|
130218
|
+
process.env[BRANCH_DECISION_ENV] = serializeBranchDecision(branchDecision);
|
|
130219
|
+
}
|
|
129458
130220
|
await mintAccessTokenIfNeeded(inputs, {
|
|
129459
130221
|
info: (message) => actionCore.info(message),
|
|
129460
130222
|
warning: (message) => actionCore.warning(message)
|