@postman-cse/onboarding-repo-sync 2.1.5 → 2.1.7
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 +1003 -138
- package/dist/cli.cjs +1459 -142
- package/dist/index.cjs +1013 -138
- 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
|
|
@@ -125114,12 +125114,14 @@ var POSTMAN_ENDPOINT_PROFILES = {
|
|
|
125114
125114
|
prod: {
|
|
125115
125115
|
apiBaseUrl: "https://api.getpostman.com",
|
|
125116
125116
|
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
125117
|
+
fallbackBaseUrl: "https://go.postman.co/_api",
|
|
125117
125118
|
cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
|
|
125118
125119
|
iapubBaseUrl: "https://iapub.postman.co"
|
|
125119
125120
|
},
|
|
125120
125121
|
beta: {
|
|
125121
125122
|
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
125122
125123
|
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
125124
|
+
fallbackBaseUrl: "https://go.postman-beta.co/_api",
|
|
125123
125125
|
cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
|
|
125124
125126
|
iapubBaseUrl: "https://iapub.postman.co"
|
|
125125
125127
|
}
|
|
@@ -125186,6 +125188,9 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125186
125188
|
" branches: [main]",
|
|
125187
125189
|
" schedule:",
|
|
125188
125190
|
' - cron: "0 */6 * * *"',
|
|
125191
|
+
"concurrency:",
|
|
125192
|
+
" group: postman-onboard-${{ github.head_ref || github.ref_name }}",
|
|
125193
|
+
" cancel-in-progress: false",
|
|
125189
125194
|
"jobs:",
|
|
125190
125195
|
" test:",
|
|
125191
125196
|
" runs-on: ubuntu-latest",
|
|
@@ -125202,7 +125207,7 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125202
125207
|
" ruby <<'RUBY'",
|
|
125203
125208
|
" require 'yaml'",
|
|
125204
125209
|
" config = YAML.load_file('.postman/resources.yaml') || {}",
|
|
125205
|
-
" cloud = config.fetch('cloudResources', {})",
|
|
125210
|
+
" cloud = config.fetch('canonical', config.fetch('cloudResources', {}))",
|
|
125206
125211
|
" collections = cloud.fetch('collections', {})",
|
|
125207
125212
|
" environments = cloud.fetch('environments', {})",
|
|
125208
125213
|
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
@@ -125400,6 +125405,58 @@ function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125400
125405
|
""
|
|
125401
125406
|
];
|
|
125402
125407
|
}
|
|
125408
|
+
function renderGcWorkflowTemplate() {
|
|
125409
|
+
return [
|
|
125410
|
+
"name: Postman Preview GC",
|
|
125411
|
+
"on:",
|
|
125412
|
+
" delete:",
|
|
125413
|
+
' branches: ["**"]',
|
|
125414
|
+
" pull_request:",
|
|
125415
|
+
" types: [closed]",
|
|
125416
|
+
" schedule:",
|
|
125417
|
+
' - cron: "0 2 * * *"',
|
|
125418
|
+
" workflow_dispatch:",
|
|
125419
|
+
" inputs:",
|
|
125420
|
+
" branch:",
|
|
125421
|
+
" description: Branch name to GC (optional, otherwise sweep by TTL/branch-existence)",
|
|
125422
|
+
" required: false",
|
|
125423
|
+
"concurrency:",
|
|
125424
|
+
" group: postman-preview-gc-${{ github.ref_name }}",
|
|
125425
|
+
" cancel-in-progress: false",
|
|
125426
|
+
"jobs:",
|
|
125427
|
+
" gc:",
|
|
125428
|
+
" runs-on: ubuntu-latest",
|
|
125429
|
+
" if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
125430
|
+
" steps:",
|
|
125431
|
+
" - uses: actions/checkout@v5",
|
|
125432
|
+
" with:",
|
|
125433
|
+
" fetch-depth: 0",
|
|
125434
|
+
" - name: Run Postman preview GC (provider-neutral cli.cjs gc)",
|
|
125435
|
+
" env:",
|
|
125436
|
+
" POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}",
|
|
125437
|
+
" POSTMAN_ACCESS_TOKEN: ${{ secrets.POSTMAN_ACCESS_TOKEN }}",
|
|
125438
|
+
" POSTMAN_WORKSPACE_ID: ${{ vars.POSTMAN_WORKSPACE_ID }}",
|
|
125439
|
+
" REPO_URL: https://github.com/${{ github.repository }}",
|
|
125440
|
+
" run: |",
|
|
125441
|
+
" set -euo pipefail",
|
|
125442
|
+
" # The repo-sync action bundles a provider-neutral gc command (cli.cjs gc)",
|
|
125443
|
+
" # that uses the Postman access token for inventory/deletion and the",
|
|
125444
|
+
" # provider ambient credential (GITHUB_TOKEN via git ls-remote) for branch existence.",
|
|
125445
|
+
" # Daily scheduled run is the retention executor (TTL contract of last resort).",
|
|
125446
|
+
' if [ -n "${{ inputs.branch }}" ]; then',
|
|
125447
|
+
' npx @postman-cse/onboarding-repo-sync gc --branch "${{ inputs.branch }}" --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125448
|
+
" else",
|
|
125449
|
+
' npx @postman-cse/onboarding-repo-sync gc --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125450
|
+
" fi",
|
|
125451
|
+
" - name: Orphan audit summary (job summary)",
|
|
125452
|
+
" if: always()",
|
|
125453
|
+
" run: |",
|
|
125454
|
+
' echo "### Preview GC orphan audit" >> "$GITHUB_STEP_SUMMARY"',
|
|
125455
|
+
' echo "Marker-guarded deletion only \u2014 strangers (no marker) are never deleted. See gc-summary-json for structured counts." >> "$GITHUB_STEP_SUMMARY"',
|
|
125456
|
+
""
|
|
125457
|
+
].join("\n");
|
|
125458
|
+
}
|
|
125459
|
+
var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
|
|
125403
125460
|
function getCiWorkflowTemplate(provider, options = {}) {
|
|
125404
125461
|
if (provider === "azure-devops") {
|
|
125405
125462
|
const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
|
|
@@ -126970,6 +127027,53 @@ function createInternalIntegrationAdapter(options) {
|
|
|
126970
127027
|
return new BifrostInternalIntegrationAdapter(options);
|
|
126971
127028
|
}
|
|
126972
127029
|
|
|
127030
|
+
// src/lib/retry.ts
|
|
127031
|
+
function sleep(delayMs) {
|
|
127032
|
+
return new Promise((resolve3) => {
|
|
127033
|
+
setTimeout(resolve3, delayMs);
|
|
127034
|
+
});
|
|
127035
|
+
}
|
|
127036
|
+
function normalizeRetryOptions(options) {
|
|
127037
|
+
return {
|
|
127038
|
+
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
127039
|
+
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
127040
|
+
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
127041
|
+
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
127042
|
+
onRetry: options.onRetry ?? (async () => void 0),
|
|
127043
|
+
shouldRetry: options.shouldRetry ?? (() => true),
|
|
127044
|
+
sleep: options.sleep ?? sleep
|
|
127045
|
+
};
|
|
127046
|
+
}
|
|
127047
|
+
async function retry(operation, options = {}) {
|
|
127048
|
+
const normalized = normalizeRetryOptions(options);
|
|
127049
|
+
let nextDelayMs = normalized.delayMs;
|
|
127050
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
127051
|
+
try {
|
|
127052
|
+
return await operation();
|
|
127053
|
+
} catch (error2) {
|
|
127054
|
+
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
127055
|
+
attempt,
|
|
127056
|
+
maxAttempts: normalized.maxAttempts
|
|
127057
|
+
});
|
|
127058
|
+
if (!shouldRetry) {
|
|
127059
|
+
throw error2;
|
|
127060
|
+
}
|
|
127061
|
+
await normalized.onRetry({
|
|
127062
|
+
attempt,
|
|
127063
|
+
maxAttempts: normalized.maxAttempts,
|
|
127064
|
+
delayMs: nextDelayMs,
|
|
127065
|
+
error: error2
|
|
127066
|
+
});
|
|
127067
|
+
await normalized.sleep(nextDelayMs);
|
|
127068
|
+
nextDelayMs = Math.min(
|
|
127069
|
+
normalized.maxDelayMs,
|
|
127070
|
+
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
127071
|
+
);
|
|
127072
|
+
}
|
|
127073
|
+
}
|
|
127074
|
+
throw new Error("Retry exhausted without returning or throwing");
|
|
127075
|
+
}
|
|
127076
|
+
|
|
126973
127077
|
// src/contracts.ts
|
|
126974
127078
|
var postmanRepoSyncActionContract = {
|
|
126975
127079
|
name: "postman-repo-sync-action",
|
|
@@ -127139,6 +127243,25 @@ var postmanRepoSyncActionContract = {
|
|
|
127139
127243
|
default: "warn",
|
|
127140
127244
|
allowedValues: ["enforce", "warn"]
|
|
127141
127245
|
},
|
|
127246
|
+
"branch-strategy": {
|
|
127247
|
+
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.",
|
|
127248
|
+
required: false,
|
|
127249
|
+
default: "legacy",
|
|
127250
|
+
allowedValues: ["legacy", "preview", "publish-gate"]
|
|
127251
|
+
},
|
|
127252
|
+
"canonical-branch": {
|
|
127253
|
+
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.",
|
|
127254
|
+
required: false
|
|
127255
|
+
},
|
|
127256
|
+
"channels": {
|
|
127257
|
+
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.',
|
|
127258
|
+
required: false
|
|
127259
|
+
},
|
|
127260
|
+
"preview-ttl": {
|
|
127261
|
+
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).",
|
|
127262
|
+
required: false,
|
|
127263
|
+
default: "30"
|
|
127264
|
+
},
|
|
127142
127265
|
"github-token": {
|
|
127143
127266
|
description: "GitHub token used for repo variable persistence and commits.",
|
|
127144
127267
|
required: false
|
|
@@ -127176,6 +127299,11 @@ var postmanRepoSyncActionContract = {
|
|
|
127176
127299
|
description: "Spec UID from bootstrap, persisted into .postman/resources.yaml cloudResources.",
|
|
127177
127300
|
required: false
|
|
127178
127301
|
},
|
|
127302
|
+
"spec-content-changed": {
|
|
127303
|
+
description: "Whether bootstrap changed canonical spec content; controls final native Spec Hub tag publication.",
|
|
127304
|
+
required: false,
|
|
127305
|
+
default: "true"
|
|
127306
|
+
},
|
|
127179
127307
|
"spec-path": {
|
|
127180
127308
|
description: "Optional repo-root-relative path to the local spec file for resources/workflows metadata.",
|
|
127181
127309
|
required: false
|
|
@@ -127220,6 +127348,18 @@ var postmanRepoSyncActionContract = {
|
|
|
127220
127348
|
},
|
|
127221
127349
|
"commit-sha": {
|
|
127222
127350
|
description: "Commit SHA produced by repo-write-mode, if any."
|
|
127351
|
+
},
|
|
127352
|
+
"sync-status": {
|
|
127353
|
+
description: "Branch-aware sync status: synced, skipped-branch-gate, or empty under branch-strategy legacy."
|
|
127354
|
+
},
|
|
127355
|
+
"branch-decision": {
|
|
127356
|
+
description: "Serialized BranchDecision JSON for downstream actions (also exported as POSTMAN_BRANCH_DECISION)."
|
|
127357
|
+
},
|
|
127358
|
+
"spec-version-tag": {
|
|
127359
|
+
description: "Native Spec Hub version tag created after successful canonical repo-sync finalization."
|
|
127360
|
+
},
|
|
127361
|
+
"spec-version-url": {
|
|
127362
|
+
description: "Read-only URL for the tagged Spec Hub snapshot."
|
|
127223
127363
|
}
|
|
127224
127364
|
},
|
|
127225
127365
|
behavior: {
|
|
@@ -127287,53 +127427,6 @@ var PostmanAssetsClient = class {
|
|
|
127287
127427
|
}
|
|
127288
127428
|
};
|
|
127289
127429
|
|
|
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
127430
|
// src/lib/postman/postman-gateway-assets-client.ts
|
|
127338
127431
|
var MAX_CREATE_FLIGHTS = 256;
|
|
127339
127432
|
var createFlights = /* @__PURE__ */ new Map();
|
|
@@ -127359,6 +127452,84 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127359
127452
|
if (!envelope) return null;
|
|
127360
127453
|
return this.asRecord(envelope.data) ?? envelope;
|
|
127361
127454
|
}
|
|
127455
|
+
/** Native Spec Hub tags attach to the latest changelog group. */
|
|
127456
|
+
async tagSpecVersion(specId, name) {
|
|
127457
|
+
const trimmed = name.trim().slice(0, 255);
|
|
127458
|
+
const response = await this.gateway.requestJson({
|
|
127459
|
+
service: "specification",
|
|
127460
|
+
method: "post",
|
|
127461
|
+
path: `/specifications/${specId}/tags`,
|
|
127462
|
+
body: { name: trimmed }
|
|
127463
|
+
});
|
|
127464
|
+
const record = this.dataOf(response) ?? {};
|
|
127465
|
+
return { id: String(record.id ?? "").trim(), name: String(record.name ?? trimmed).trim() };
|
|
127466
|
+
}
|
|
127467
|
+
async listSpecVersionTags(specId) {
|
|
127468
|
+
const response = await this.gateway.requestJson({
|
|
127469
|
+
service: "specification",
|
|
127470
|
+
method: "get",
|
|
127471
|
+
path: `/specifications/${specId}/tags`,
|
|
127472
|
+
query: { limit: "50" }
|
|
127473
|
+
});
|
|
127474
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127475
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({
|
|
127476
|
+
id: String(entry.id ?? "").trim(),
|
|
127477
|
+
// listTags returns `message`; createTag returns `name`. Accept both.
|
|
127478
|
+
name: String(entry.name ?? entry.message ?? "").trim()
|
|
127479
|
+
})).filter((entry) => entry.id || entry.name);
|
|
127480
|
+
}
|
|
127481
|
+
async deleteCollection(collectionUid) {
|
|
127482
|
+
const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
|
|
127483
|
+
await this.gateway.requestJson({
|
|
127484
|
+
service: "collection",
|
|
127485
|
+
method: "delete",
|
|
127486
|
+
path: `/v3/collections/${bareId}`
|
|
127487
|
+
});
|
|
127488
|
+
}
|
|
127489
|
+
async listSpecifications(workspaceId) {
|
|
127490
|
+
const response = await this.gateway.requestJson({
|
|
127491
|
+
service: "specification",
|
|
127492
|
+
method: "get",
|
|
127493
|
+
path: `/specifications?containerType=workspace&containerId=${workspaceId}`
|
|
127494
|
+
});
|
|
127495
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127496
|
+
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);
|
|
127497
|
+
}
|
|
127498
|
+
async getSpecContent(specId) {
|
|
127499
|
+
const files = await this.gateway.requestJson({
|
|
127500
|
+
service: "specification",
|
|
127501
|
+
method: "get",
|
|
127502
|
+
path: `/specifications/${specId}/files`
|
|
127503
|
+
});
|
|
127504
|
+
const entries = Array.isArray(files?.data) ? files.data : [];
|
|
127505
|
+
const root = entries.map((entry) => this.asRecord(entry)).find((entry) => entry?.type === "ROOT") ?? entries.map((entry) => this.asRecord(entry)).find((entry) => entry !== null);
|
|
127506
|
+
const fileId = String(root?.id ?? "").trim();
|
|
127507
|
+
if (!fileId) return void 0;
|
|
127508
|
+
const file = await this.gateway.requestJson({
|
|
127509
|
+
service: "specification",
|
|
127510
|
+
method: "get",
|
|
127511
|
+
path: `/specifications/${specId}/files/${fileId}`,
|
|
127512
|
+
query: { fields: "content" }
|
|
127513
|
+
});
|
|
127514
|
+
const record = this.dataOf(file);
|
|
127515
|
+
return typeof record?.content === "string" ? record.content : void 0;
|
|
127516
|
+
}
|
|
127517
|
+
async listSpecCollections(specId) {
|
|
127518
|
+
const response = await this.gateway.requestJson({
|
|
127519
|
+
service: "specification",
|
|
127520
|
+
method: "get",
|
|
127521
|
+
path: `/specifications/${specId}/collections`
|
|
127522
|
+
});
|
|
127523
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127524
|
+
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);
|
|
127525
|
+
}
|
|
127526
|
+
async deleteSpec(specId) {
|
|
127527
|
+
await this.gateway.requestJson({
|
|
127528
|
+
service: "specification",
|
|
127529
|
+
method: "delete",
|
|
127530
|
+
path: `/specifications/${specId}`
|
|
127531
|
+
});
|
|
127532
|
+
}
|
|
127362
127533
|
idOf(record) {
|
|
127363
127534
|
if (!record) return "";
|
|
127364
127535
|
const id = record.uid ?? record.id;
|
|
@@ -127418,6 +127589,14 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127418
127589
|
createFlights.set(key, { fingerprint, promise: pending });
|
|
127419
127590
|
return pending;
|
|
127420
127591
|
}
|
|
127592
|
+
/**
|
|
127593
|
+
* Reconcile an ambiguous create. Returns the adopted match, `undefined`
|
|
127594
|
+
* when every discovery read succeeded and found nothing (the create is
|
|
127595
|
+
* conclusively absent, so a fallback resend cannot duplicate), or `null`
|
|
127596
|
+
* when the outcome is inconclusive (non-ambiguous error, or a discovery
|
|
127597
|
+
* read itself failed — in that case a resend could duplicate a committed
|
|
127598
|
+
* twin and must not fire).
|
|
127599
|
+
*/
|
|
127421
127600
|
async discoverAfterAmbiguousCreate(discover, error2) {
|
|
127422
127601
|
if (!this.isAmbiguousCreateOutcome(error2)) {
|
|
127423
127602
|
return null;
|
|
@@ -127426,12 +127605,31 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127426
127605
|
if (attempt > 0) {
|
|
127427
127606
|
await this.sleep(this.reconcileDelayMs);
|
|
127428
127607
|
}
|
|
127429
|
-
|
|
127608
|
+
let found;
|
|
127609
|
+
try {
|
|
127610
|
+
found = await discover();
|
|
127611
|
+
} catch {
|
|
127612
|
+
return null;
|
|
127613
|
+
}
|
|
127430
127614
|
if (found) {
|
|
127431
127615
|
return found;
|
|
127432
127616
|
}
|
|
127433
127617
|
}
|
|
127434
|
-
return
|
|
127618
|
+
return void 0;
|
|
127619
|
+
}
|
|
127620
|
+
/**
|
|
127621
|
+
* After an ambiguous create failed AND discovery proved the asset absent,
|
|
127622
|
+
* re-attempt the create once with the cold `/_api` fallback enabled: the
|
|
127623
|
+
* reconcile above guarantees no committed twin, so the resend cannot
|
|
127624
|
+
* duplicate. Returns null when the resend also fails (caller rethrows the
|
|
127625
|
+
* original error).
|
|
127626
|
+
*/
|
|
127627
|
+
async resendAbsentCreate(operation) {
|
|
127628
|
+
try {
|
|
127629
|
+
return await operation();
|
|
127630
|
+
} catch {
|
|
127631
|
+
return null;
|
|
127632
|
+
}
|
|
127435
127633
|
}
|
|
127436
127634
|
publicEnvironmentUid(data, bareId) {
|
|
127437
127635
|
const owner = String(data?.owner ?? "").trim();
|
|
@@ -127479,17 +127677,18 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127479
127677
|
name: envName,
|
|
127480
127678
|
values: normalizedValues
|
|
127481
127679
|
};
|
|
127680
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
127681
|
+
{
|
|
127682
|
+
service: "sync",
|
|
127683
|
+
method: "post",
|
|
127684
|
+
path: `/environment/import?workspace=${ws}`,
|
|
127685
|
+
body,
|
|
127686
|
+
...fallback ? { fallback } : {}
|
|
127687
|
+
},
|
|
127688
|
+
{ retryTransient: false }
|
|
127689
|
+
);
|
|
127482
127690
|
try {
|
|
127483
|
-
const
|
|
127484
|
-
{
|
|
127485
|
-
service: "sync",
|
|
127486
|
-
method: "post",
|
|
127487
|
-
path: `/environment/import?workspace=${ws}`,
|
|
127488
|
-
body
|
|
127489
|
-
},
|
|
127490
|
-
{ retryTransient: false }
|
|
127491
|
-
);
|
|
127492
|
-
const data = this.dataOf(response);
|
|
127691
|
+
const data = this.dataOf(await send2());
|
|
127493
127692
|
const bareId = this.idOf(data);
|
|
127494
127693
|
if (!bareId) {
|
|
127495
127694
|
throw new Error("Environment import did not return a UID");
|
|
@@ -127504,6 +127703,15 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127504
127703
|
await this.updateEnvironment(adopted, envName, values);
|
|
127505
127704
|
return adopted;
|
|
127506
127705
|
}
|
|
127706
|
+
if (adopted === void 0) {
|
|
127707
|
+
const retried = await this.resendAbsentCreate(async () => {
|
|
127708
|
+
const data = this.dataOf(await send2("auto"));
|
|
127709
|
+
const bareId = this.idOf(data);
|
|
127710
|
+
if (!bareId) throw new Error("Environment import did not return a UID");
|
|
127711
|
+
return this.publicEnvironmentUid(data, bareId);
|
|
127712
|
+
});
|
|
127713
|
+
if (retried) return retried;
|
|
127714
|
+
}
|
|
127507
127715
|
throw error2;
|
|
127508
127716
|
}
|
|
127509
127717
|
});
|
|
@@ -127635,16 +127843,20 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127635
127843
|
private: false,
|
|
127636
127844
|
...environment ? { environment } : {}
|
|
127637
127845
|
};
|
|
127638
|
-
|
|
127639
|
-
|
|
127640
|
-
|
|
127641
|
-
|
|
127642
|
-
|
|
127643
|
-
|
|
127644
|
-
|
|
127645
|
-
|
|
127646
|
-
|
|
127647
|
-
|
|
127846
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
127847
|
+
{
|
|
127848
|
+
service: "mock",
|
|
127849
|
+
method: "post",
|
|
127850
|
+
path: `/mocks?workspace=${ws}`,
|
|
127851
|
+
body,
|
|
127852
|
+
...fallback ? { fallback } : {}
|
|
127853
|
+
},
|
|
127854
|
+
// Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
|
|
127855
|
+
// may still have created the mock; reconcile via discovery below and
|
|
127856
|
+
// let the orchestrator retry the whole create-or-adopt cycle.
|
|
127857
|
+
{ retryTransient: false }
|
|
127858
|
+
);
|
|
127859
|
+
const parseMock = (response) => {
|
|
127648
127860
|
const record = this.dataOf(response);
|
|
127649
127861
|
const uid = this.idOf(record);
|
|
127650
127862
|
if (!uid) {
|
|
@@ -127654,6 +127866,9 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127654
127866
|
uid,
|
|
127655
127867
|
url: String(record?.url ?? record?.mockUrl ?? "").trim()
|
|
127656
127868
|
};
|
|
127869
|
+
};
|
|
127870
|
+
try {
|
|
127871
|
+
return parseMock(await send2());
|
|
127657
127872
|
} catch (error2) {
|
|
127658
127873
|
const adopted = await this.discoverAfterAmbiguousCreate(
|
|
127659
127874
|
() => this.findMockByCollection(collection, environment, mockName),
|
|
@@ -127662,6 +127877,10 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127662
127877
|
if (adopted) {
|
|
127663
127878
|
return { uid: adopted.uid, url: adopted.mockUrl };
|
|
127664
127879
|
}
|
|
127880
|
+
if (adopted === void 0) {
|
|
127881
|
+
const retried = await this.resendAbsentCreate(async () => parseMock(await send2("auto")));
|
|
127882
|
+
if (retried) return retried;
|
|
127883
|
+
}
|
|
127665
127884
|
throw error2;
|
|
127666
127885
|
}
|
|
127667
127886
|
});
|
|
@@ -127681,6 +127900,33 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127681
127900
|
environment: String(m.environment ?? "")
|
|
127682
127901
|
}));
|
|
127683
127902
|
}
|
|
127903
|
+
/**
|
|
127904
|
+
* Delete an environment through the sync service (GC path). The path id is
|
|
127905
|
+
* the bare model id (public uid tail), mirroring updateEnvironment.
|
|
127906
|
+
*/
|
|
127907
|
+
async deleteEnvironment(uid) {
|
|
127908
|
+
await this.gateway.requestJson({
|
|
127909
|
+
service: "sync",
|
|
127910
|
+
method: "delete",
|
|
127911
|
+
path: `/environment/${this.toModelId(uid)}`
|
|
127912
|
+
});
|
|
127913
|
+
}
|
|
127914
|
+
/** Delete a mock server (GC path). */
|
|
127915
|
+
async deleteMock(uid) {
|
|
127916
|
+
await this.gateway.requestJson({
|
|
127917
|
+
service: "mock",
|
|
127918
|
+
method: "delete",
|
|
127919
|
+
path: `/mocks/${this.toModelId(uid)}`
|
|
127920
|
+
});
|
|
127921
|
+
}
|
|
127922
|
+
/** Delete a collection-based monitor (jobTemplate) (GC path). */
|
|
127923
|
+
async deleteMonitor(uid) {
|
|
127924
|
+
await this.gateway.requestJson({
|
|
127925
|
+
service: "monitors",
|
|
127926
|
+
method: "delete",
|
|
127927
|
+
path: `/jobTemplates/${this.toModelId(uid)}`
|
|
127928
|
+
});
|
|
127929
|
+
}
|
|
127684
127930
|
async mockExists(uid) {
|
|
127685
127931
|
try {
|
|
127686
127932
|
await this.gateway.requestJson({
|
|
@@ -127741,21 +127987,25 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127741
127987
|
distribution: null,
|
|
127742
127988
|
...environment ? { environment } : {}
|
|
127743
127989
|
};
|
|
127744
|
-
|
|
127745
|
-
|
|
127746
|
-
|
|
127747
|
-
|
|
127748
|
-
|
|
127749
|
-
|
|
127750
|
-
|
|
127751
|
-
|
|
127752
|
-
|
|
127753
|
-
|
|
127990
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
127991
|
+
{
|
|
127992
|
+
service: "monitors",
|
|
127993
|
+
method: "post",
|
|
127994
|
+
path: `/jobTemplates?workspace=${ws}`,
|
|
127995
|
+
body,
|
|
127996
|
+
...fallback ? { fallback } : {}
|
|
127997
|
+
},
|
|
127998
|
+
{ retryTransient: false }
|
|
127999
|
+
);
|
|
128000
|
+
const parseMonitor = (response) => {
|
|
127754
128001
|
const uid = this.idOf(this.dataOf(response));
|
|
127755
128002
|
if (!uid) {
|
|
127756
128003
|
throw new Error("Monitor create did not return a UID");
|
|
127757
128004
|
}
|
|
127758
128005
|
return uid;
|
|
128006
|
+
};
|
|
128007
|
+
try {
|
|
128008
|
+
return parseMonitor(await send2());
|
|
127759
128009
|
} catch (error2) {
|
|
127760
128010
|
const adopted = await this.discoverAfterAmbiguousCreate(
|
|
127761
128011
|
() => this.findMonitorByCollection(collection, environment, monitorName),
|
|
@@ -127764,6 +128014,10 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127764
128014
|
if (adopted?.uid) {
|
|
127765
128015
|
return adopted.uid;
|
|
127766
128016
|
}
|
|
128017
|
+
if (adopted === void 0) {
|
|
128018
|
+
const retried = await this.resendAbsentCreate(async () => parseMonitor(await send2("auto")));
|
|
128019
|
+
if (retried) return retried;
|
|
128020
|
+
}
|
|
127767
128021
|
throw error2;
|
|
127768
128022
|
}
|
|
127769
128023
|
});
|
|
@@ -127992,6 +128246,7 @@ var AccessTokenGatewayClient = class {
|
|
|
127992
128246
|
fetchImpl;
|
|
127993
128247
|
secretMasker;
|
|
127994
128248
|
maxRetries;
|
|
128249
|
+
fallbackBaseUrl;
|
|
127995
128250
|
retryBaseDelayMs;
|
|
127996
128251
|
sleepImpl;
|
|
127997
128252
|
constructor(options) {
|
|
@@ -128004,6 +128259,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128004
128259
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
128005
128260
|
this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
|
|
128006
128261
|
this.maxRetries = options.maxRetries ?? 3;
|
|
128262
|
+
const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
|
|
128263
|
+
this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
|
|
128007
128264
|
this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
|
|
128008
128265
|
this.sleepImpl = options.sleepImpl ?? defaultSleep;
|
|
128009
128266
|
}
|
|
@@ -128022,8 +128279,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128022
128279
|
}
|
|
128023
128280
|
return headers;
|
|
128024
128281
|
}
|
|
128025
|
-
async send(request) {
|
|
128026
|
-
const url = `${this.bifrostBaseUrl}/ws/proxy`;
|
|
128282
|
+
async send(request, baseUrl) {
|
|
128283
|
+
const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
|
|
128027
128284
|
return this.fetchImpl(url, {
|
|
128028
128285
|
method: "POST",
|
|
128029
128286
|
headers: this.buildHeaders(request.headers),
|
|
@@ -128036,6 +128293,43 @@ var AccessTokenGatewayClient = class {
|
|
|
128036
128293
|
})
|
|
128037
128294
|
});
|
|
128038
128295
|
}
|
|
128296
|
+
/**
|
|
128297
|
+
* One cold, serial attempt against the fallback base URL after the primary
|
|
128298
|
+
* budget is exhausted on a transient failure. Never hedged in parallel with
|
|
128299
|
+
* the primary; only fires when the request would otherwise throw. Callers
|
|
128300
|
+
* with `retryTransient: false` still reconcile first — the fallback attempt
|
|
128301
|
+
* here is the resend, so it is only used for requests whose mutation is
|
|
128302
|
+
* known idempotent or already reconciled by the caller's adopt-on-ambiguous
|
|
128303
|
+
* loop.
|
|
128304
|
+
*/
|
|
128305
|
+
async tryFallback(request) {
|
|
128306
|
+
if (!this.fallbackBaseUrl) return null;
|
|
128307
|
+
try {
|
|
128308
|
+
return await this.send(request, this.fallbackBaseUrl);
|
|
128309
|
+
} catch {
|
|
128310
|
+
return null;
|
|
128311
|
+
}
|
|
128312
|
+
}
|
|
128313
|
+
/**
|
|
128314
|
+
* Run the fallback attempt and classify its response the same way the
|
|
128315
|
+
* primary path would. Returns the success response, or null when the
|
|
128316
|
+
* fallback also failed transiently (caller then throws the original error).
|
|
128317
|
+
* Non-transient fallback failures (4xx) surface as their own HttpError since
|
|
128318
|
+
* they are the freshest authoritative answer.
|
|
128319
|
+
*/
|
|
128320
|
+
fallbackEligible(request, retryTransient) {
|
|
128321
|
+
if (!this.fallbackBaseUrl) return false;
|
|
128322
|
+
return retryTransient || request.fallback === "auto";
|
|
128323
|
+
}
|
|
128324
|
+
async attemptFallback(request, retryTransient) {
|
|
128325
|
+
if (!this.fallbackEligible(request, retryTransient)) return null;
|
|
128326
|
+
const response = await this.tryFallback(request);
|
|
128327
|
+
if (!response) return null;
|
|
128328
|
+
if (response.ok) return response;
|
|
128329
|
+
const body = await response.text().catch(() => "");
|
|
128330
|
+
if (isRetryableSafeReadResponse(response.status)) return null;
|
|
128331
|
+
throw this.toHttpError(request, response, body);
|
|
128332
|
+
}
|
|
128039
128333
|
/**
|
|
128040
128334
|
* Send a gateway request, refreshing the token once on an auth failure and
|
|
128041
128335
|
* optionally retrying transient downstream failures (5xx / Bifrost read
|
|
@@ -128058,6 +128352,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128058
128352
|
await this.sleepImpl(delay);
|
|
128059
128353
|
continue;
|
|
128060
128354
|
}
|
|
128355
|
+
const fallbackResponse2 = await this.attemptFallback(request, retryTransient);
|
|
128356
|
+
if (fallbackResponse2) return fallbackResponse2;
|
|
128061
128357
|
throw error2;
|
|
128062
128358
|
}
|
|
128063
128359
|
if (response.ok) {
|
|
@@ -128079,6 +128375,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128079
128375
|
await this.sleepImpl(delay);
|
|
128080
128376
|
continue;
|
|
128081
128377
|
}
|
|
128378
|
+
const fallbackResponse = await this.attemptFallback(request, retryTransient);
|
|
128379
|
+
if (fallbackResponse) return fallbackResponse;
|
|
128082
128380
|
throw this.toHttpError(request, response, body);
|
|
128083
128381
|
}
|
|
128084
128382
|
}
|
|
@@ -128187,6 +128485,331 @@ function validateCertMaterial(certBase64, keyBase64, passphrase) {
|
|
|
128187
128485
|
}
|
|
128188
128486
|
}
|
|
128189
128487
|
|
|
128488
|
+
// src/lib/repo/branch-decision.ts
|
|
128489
|
+
var import_node_fs3 = require("node:fs");
|
|
128490
|
+
var import_node_crypto3 = require("node:crypto");
|
|
128491
|
+
var ContractError = class extends Error {
|
|
128492
|
+
code;
|
|
128493
|
+
constructor(code, message) {
|
|
128494
|
+
super(`${code}: ${message}`);
|
|
128495
|
+
this.code = code;
|
|
128496
|
+
this.name = "ContractError";
|
|
128497
|
+
}
|
|
128498
|
+
};
|
|
128499
|
+
function clean(value) {
|
|
128500
|
+
const trimmed = (value ?? "").trim();
|
|
128501
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
128502
|
+
}
|
|
128503
|
+
function stripRefPrefix(ref) {
|
|
128504
|
+
const raw = clean(ref);
|
|
128505
|
+
if (!raw) {
|
|
128506
|
+
return { kind: "unknown" };
|
|
128507
|
+
}
|
|
128508
|
+
if (raw.startsWith("refs/heads/")) {
|
|
128509
|
+
return { name: raw.slice("refs/heads/".length), kind: "branch" };
|
|
128510
|
+
}
|
|
128511
|
+
if (raw.startsWith("refs/tags/")) {
|
|
128512
|
+
return { name: raw.slice("refs/tags/".length), kind: "tag" };
|
|
128513
|
+
}
|
|
128514
|
+
if (raw.startsWith("refs/pull/") || raw.startsWith("refs/merge")) {
|
|
128515
|
+
return { kind: "unknown" };
|
|
128516
|
+
}
|
|
128517
|
+
return { name: raw, kind: "branch" };
|
|
128518
|
+
}
|
|
128519
|
+
function detectProvider(env) {
|
|
128520
|
+
if (clean(env.GITHUB_ACTIONS) || clean(env.GITHUB_REPOSITORY)) return "github";
|
|
128521
|
+
if (clean(env.GITLAB_CI) || clean(env.CI_PROJECT_PATH)) return "gitlab";
|
|
128522
|
+
if (clean(env.BITBUCKET_REPO_SLUG) || clean(env.BITBUCKET_BRANCH)) return "bitbucket";
|
|
128523
|
+
if (clean(env.TF_BUILD) || clean(env.BUILD_REPOSITORY_URI)) return "azure-devops";
|
|
128524
|
+
return "unknown";
|
|
128525
|
+
}
|
|
128526
|
+
function readGithubEvent(env) {
|
|
128527
|
+
const path9 = clean(env.GITHUB_EVENT_PATH);
|
|
128528
|
+
if (!path9) return void 0;
|
|
128529
|
+
try {
|
|
128530
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(path9, "utf8"));
|
|
128531
|
+
} catch {
|
|
128532
|
+
return void 0;
|
|
128533
|
+
}
|
|
128534
|
+
}
|
|
128535
|
+
function resolveBranchIdentity(env = process.env, overrides = {}) {
|
|
128536
|
+
const provider = detectProvider(env);
|
|
128537
|
+
const explicitDefault = clean(overrides.defaultBranch);
|
|
128538
|
+
let headBranch;
|
|
128539
|
+
let rawRef;
|
|
128540
|
+
let refKind;
|
|
128541
|
+
let isPrContext = false;
|
|
128542
|
+
let isForkPr = false;
|
|
128543
|
+
let defaultBranch = explicitDefault;
|
|
128544
|
+
let headSha;
|
|
128545
|
+
switch (provider) {
|
|
128546
|
+
case "github": {
|
|
128547
|
+
const event = readGithubEvent(env);
|
|
128548
|
+
headSha = clean(env.GITHUB_SHA);
|
|
128549
|
+
defaultBranch ??= clean(event?.repository?.default_branch);
|
|
128550
|
+
const headRef = clean(env.GITHUB_HEAD_REF);
|
|
128551
|
+
if (headRef) {
|
|
128552
|
+
isPrContext = true;
|
|
128553
|
+
headBranch = headRef;
|
|
128554
|
+
rawRef = clean(env.GITHUB_REF) ?? headRef;
|
|
128555
|
+
refKind = "branch";
|
|
128556
|
+
const headRepo = event?.pull_request?.head?.repo?.full_name;
|
|
128557
|
+
const baseRepo = event?.pull_request?.base?.repo?.full_name ?? event?.repository?.full_name;
|
|
128558
|
+
isForkPr = Boolean(headRepo && baseRepo && headRepo !== baseRepo);
|
|
128559
|
+
headSha = clean(event?.pull_request?.head?.sha) ?? headSha;
|
|
128560
|
+
} else {
|
|
128561
|
+
rawRef = clean(env.GITHUB_REF) ?? clean(env.GITHUB_REF_NAME);
|
|
128562
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128563
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128564
|
+
refKind = parsed.kind;
|
|
128565
|
+
}
|
|
128566
|
+
break;
|
|
128567
|
+
}
|
|
128568
|
+
case "gitlab": {
|
|
128569
|
+
headSha = clean(env.CI_COMMIT_SHA);
|
|
128570
|
+
defaultBranch ??= clean(env.CI_DEFAULT_BRANCH);
|
|
128571
|
+
const mrSource = clean(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME);
|
|
128572
|
+
if (mrSource) {
|
|
128573
|
+
isPrContext = true;
|
|
128574
|
+
headBranch = mrSource;
|
|
128575
|
+
rawRef = mrSource;
|
|
128576
|
+
refKind = "branch";
|
|
128577
|
+
const sourceProject = clean(env.CI_MERGE_REQUEST_SOURCE_PROJECT_ID);
|
|
128578
|
+
const targetProject = clean(env.CI_MERGE_REQUEST_PROJECT_ID);
|
|
128579
|
+
isForkPr = Boolean(sourceProject && targetProject && sourceProject !== targetProject);
|
|
128580
|
+
} else if (clean(env.CI_COMMIT_TAG)) {
|
|
128581
|
+
rawRef = clean(env.CI_COMMIT_TAG);
|
|
128582
|
+
refKind = "tag";
|
|
128583
|
+
} else {
|
|
128584
|
+
headBranch = clean(env.CI_COMMIT_BRANCH) ?? clean(env.CI_COMMIT_REF_NAME);
|
|
128585
|
+
rawRef = headBranch;
|
|
128586
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128587
|
+
}
|
|
128588
|
+
break;
|
|
128589
|
+
}
|
|
128590
|
+
case "bitbucket": {
|
|
128591
|
+
headSha = clean(env.BITBUCKET_COMMIT);
|
|
128592
|
+
if (clean(env.BITBUCKET_TAG)) {
|
|
128593
|
+
rawRef = clean(env.BITBUCKET_TAG);
|
|
128594
|
+
refKind = "tag";
|
|
128595
|
+
} else {
|
|
128596
|
+
headBranch = clean(env.BITBUCKET_BRANCH);
|
|
128597
|
+
rawRef = headBranch;
|
|
128598
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128599
|
+
isPrContext = Boolean(clean(env.BITBUCKET_PR_ID));
|
|
128600
|
+
}
|
|
128601
|
+
break;
|
|
128602
|
+
}
|
|
128603
|
+
case "azure-devops": {
|
|
128604
|
+
headSha = clean(env.BUILD_SOURCEVERSION);
|
|
128605
|
+
const prSource = clean(env.SYSTEM_PULLREQUEST_SOURCEBRANCH);
|
|
128606
|
+
if (prSource) {
|
|
128607
|
+
isPrContext = true;
|
|
128608
|
+
const parsed = stripRefPrefix(prSource);
|
|
128609
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128610
|
+
rawRef = prSource;
|
|
128611
|
+
refKind = parsed.kind;
|
|
128612
|
+
const forkFlag = clean(env.SYSTEM_PULLREQUEST_ISFORK);
|
|
128613
|
+
isForkPr = forkFlag?.toLowerCase() === "true";
|
|
128614
|
+
} else {
|
|
128615
|
+
rawRef = clean(env.BUILD_SOURCEBRANCH);
|
|
128616
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128617
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128618
|
+
refKind = parsed.kind;
|
|
128619
|
+
}
|
|
128620
|
+
break;
|
|
128621
|
+
}
|
|
128622
|
+
default: {
|
|
128623
|
+
refKind = "unknown";
|
|
128624
|
+
}
|
|
128625
|
+
}
|
|
128626
|
+
if (refKind === "branch" && headBranch && defaultBranch && headBranch === defaultBranch) {
|
|
128627
|
+
refKind = "default-branch";
|
|
128628
|
+
}
|
|
128629
|
+
return { provider, headBranch, rawRef, defaultBranch, refKind, isPrContext, isForkPr, headSha };
|
|
128630
|
+
}
|
|
128631
|
+
function parseChannelRules(input) {
|
|
128632
|
+
const raw = clean(input);
|
|
128633
|
+
const rules = [];
|
|
128634
|
+
for (const part of raw ? raw.split(",") : []) {
|
|
128635
|
+
const entry = part.trim();
|
|
128636
|
+
if (!entry) continue;
|
|
128637
|
+
const eq = entry.indexOf("=");
|
|
128638
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
128639
|
+
throw new ContractError(
|
|
128640
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128641
|
+
`channels entry "${entry}" must be <branch-or-glob>=<CODE>`
|
|
128642
|
+
);
|
|
128643
|
+
}
|
|
128644
|
+
const pattern = entry.slice(0, eq).trim();
|
|
128645
|
+
const code = entry.slice(eq + 1).trim().toUpperCase();
|
|
128646
|
+
if (!/^[A-Z][A-Z0-9_-]{0,15}$/.test(code)) {
|
|
128647
|
+
throw new ContractError(
|
|
128648
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128649
|
+
`channel code "${code}" must be 1-16 chars, A-Z 0-9 _ -, starting with a letter`
|
|
128650
|
+
);
|
|
128651
|
+
}
|
|
128652
|
+
rules.push({ pattern, code });
|
|
128653
|
+
}
|
|
128654
|
+
if (!rules.some((rule) => rule.pattern === "release/*")) {
|
|
128655
|
+
rules.push({ pattern: "release/*", code: "RC" });
|
|
128656
|
+
}
|
|
128657
|
+
return rules;
|
|
128658
|
+
}
|
|
128659
|
+
function matchChannel(branch, rules) {
|
|
128660
|
+
for (const rule of rules) {
|
|
128661
|
+
if (rule.pattern.endsWith("*")) {
|
|
128662
|
+
const prefix = rule.pattern.slice(0, -1);
|
|
128663
|
+
if (branch.startsWith(prefix)) return rule;
|
|
128664
|
+
} else if (branch === rule.pattern) {
|
|
128665
|
+
return rule;
|
|
128666
|
+
}
|
|
128667
|
+
}
|
|
128668
|
+
return void 0;
|
|
128669
|
+
}
|
|
128670
|
+
function resolveBranchDecision(options) {
|
|
128671
|
+
const { strategy, identity } = options;
|
|
128672
|
+
const channels = options.channels ?? [];
|
|
128673
|
+
if (strategy === "legacy") {
|
|
128674
|
+
return {
|
|
128675
|
+
tier: "legacy",
|
|
128676
|
+
strategy,
|
|
128677
|
+
identity,
|
|
128678
|
+
canonicalBranch: clean(options.canonicalBranch) ?? identity.defaultBranch,
|
|
128679
|
+
reason: "branch-strategy legacy: branch-blind pre-v2 behavior"
|
|
128680
|
+
};
|
|
128681
|
+
}
|
|
128682
|
+
const canonicalBranch = clean(options.canonicalBranch) ?? identity.defaultBranch;
|
|
128683
|
+
if (!canonicalBranch) {
|
|
128684
|
+
throw new ContractError(
|
|
128685
|
+
"CONTRACT_DEFAULT_BRANCH_UNRESOLVED",
|
|
128686
|
+
`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.`
|
|
128687
|
+
);
|
|
128688
|
+
}
|
|
128689
|
+
if (identity.refKind === "tag" || identity.refKind === "unknown" || !identity.headBranch) {
|
|
128690
|
+
return {
|
|
128691
|
+
tier: "gated",
|
|
128692
|
+
strategy,
|
|
128693
|
+
identity,
|
|
128694
|
+
canonicalBranch,
|
|
128695
|
+
reason: `ref kind ${identity.refKind}: never canonical/preview-eligible; no-op with annotation`
|
|
128696
|
+
};
|
|
128697
|
+
}
|
|
128698
|
+
if (identity.headBranch === canonicalBranch) {
|
|
128699
|
+
return {
|
|
128700
|
+
tier: "canonical",
|
|
128701
|
+
strategy,
|
|
128702
|
+
identity,
|
|
128703
|
+
canonicalBranch,
|
|
128704
|
+
reason: `head branch equals canonical branch ${canonicalBranch}`
|
|
128705
|
+
};
|
|
128706
|
+
}
|
|
128707
|
+
const channel = matchChannel(identity.headBranch, channels);
|
|
128708
|
+
if (channel) {
|
|
128709
|
+
return {
|
|
128710
|
+
tier: "channel",
|
|
128711
|
+
strategy,
|
|
128712
|
+
identity,
|
|
128713
|
+
canonicalBranch,
|
|
128714
|
+
channel,
|
|
128715
|
+
reason: `branch ${identity.headBranch} matches channel ${channel.pattern}=${channel.code}`
|
|
128716
|
+
};
|
|
128717
|
+
}
|
|
128718
|
+
if (strategy === "preview") {
|
|
128719
|
+
if (identity.isForkPr) {
|
|
128720
|
+
return {
|
|
128721
|
+
tier: "gated",
|
|
128722
|
+
strategy,
|
|
128723
|
+
identity,
|
|
128724
|
+
canonicalBranch,
|
|
128725
|
+
reason: "fork PR: preview-ineligible (same-repo gate), gated instead"
|
|
128726
|
+
};
|
|
128727
|
+
}
|
|
128728
|
+
return {
|
|
128729
|
+
tier: "preview",
|
|
128730
|
+
strategy,
|
|
128731
|
+
identity,
|
|
128732
|
+
canonicalBranch,
|
|
128733
|
+
reason: `branch ${identity.headBranch} under branch-strategy preview`
|
|
128734
|
+
};
|
|
128735
|
+
}
|
|
128736
|
+
return {
|
|
128737
|
+
tier: "gated",
|
|
128738
|
+
strategy,
|
|
128739
|
+
identity,
|
|
128740
|
+
canonicalBranch,
|
|
128741
|
+
reason: `branch ${identity.headBranch} under branch-strategy publish-gate`
|
|
128742
|
+
};
|
|
128743
|
+
}
|
|
128744
|
+
var BRANCH_DECISION_ENV = "POSTMAN_BRANCH_DECISION";
|
|
128745
|
+
function serializeBranchDecision(decision) {
|
|
128746
|
+
return JSON.stringify(decision);
|
|
128747
|
+
}
|
|
128748
|
+
function parseBranchDecision(raw) {
|
|
128749
|
+
const value = clean(raw);
|
|
128750
|
+
if (!value) return void 0;
|
|
128751
|
+
let parsed;
|
|
128752
|
+
try {
|
|
128753
|
+
parsed = JSON.parse(value);
|
|
128754
|
+
} catch {
|
|
128755
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION is not valid JSON");
|
|
128756
|
+
}
|
|
128757
|
+
const candidate = parsed;
|
|
128758
|
+
const tiers = ["canonical", "channel", "preview", "gated", "legacy"];
|
|
128759
|
+
if (!candidate || typeof candidate !== "object" || !tiers.includes(candidate.tier) || !candidate.identity) {
|
|
128760
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION does not carry a valid BranchDecision");
|
|
128761
|
+
}
|
|
128762
|
+
return candidate;
|
|
128763
|
+
}
|
|
128764
|
+
function resolveEffectiveBranchDecision(options, env = process.env) {
|
|
128765
|
+
const inherited = parseBranchDecision(env[BRANCH_DECISION_ENV]);
|
|
128766
|
+
if (inherited) return inherited;
|
|
128767
|
+
return resolveBranchDecision(options);
|
|
128768
|
+
}
|
|
128769
|
+
var PREVIEW_SLUG_MAX = 30;
|
|
128770
|
+
function buildBranchSlug(rawBranch) {
|
|
128771
|
+
const sanitized = rawBranch.replace(/^refs\/heads\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128772
|
+
const truncated = sanitized.slice(0, PREVIEW_SLUG_MAX);
|
|
128773
|
+
const lossy = truncated !== rawBranch.replace(/^refs\/heads\//, "");
|
|
128774
|
+
if (!lossy) {
|
|
128775
|
+
return { suffix: truncated, slug: truncated, lossy };
|
|
128776
|
+
}
|
|
128777
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(rawBranch).digest("hex").slice(0, 6);
|
|
128778
|
+
return { suffix: `${truncated}-${hash}`, slug: truncated, lossy };
|
|
128779
|
+
}
|
|
128780
|
+
function previewAssetName(baseName, rawBranch) {
|
|
128781
|
+
return `${baseName} @${buildBranchSlug(rawBranch).suffix}`;
|
|
128782
|
+
}
|
|
128783
|
+
function channelAssetName(baseName, code) {
|
|
128784
|
+
return `[${code}] ${baseName}`;
|
|
128785
|
+
}
|
|
128786
|
+
var MARKER_KEY = "x-pm-onboarding";
|
|
128787
|
+
function parseAssetMarker(description) {
|
|
128788
|
+
if (!description) return void 0;
|
|
128789
|
+
const index = description.indexOf(`${MARKER_KEY}:`);
|
|
128790
|
+
if (index === -1) return void 0;
|
|
128791
|
+
const jsonStart = description.indexOf("{", index);
|
|
128792
|
+
if (jsonStart === -1) return void 0;
|
|
128793
|
+
let depth = 0;
|
|
128794
|
+
for (let i = jsonStart; i < description.length; i += 1) {
|
|
128795
|
+
const ch = description[i];
|
|
128796
|
+
if (ch === "{") depth += 1;
|
|
128797
|
+
else if (ch === "}") {
|
|
128798
|
+
depth -= 1;
|
|
128799
|
+
if (depth === 0) {
|
|
128800
|
+
try {
|
|
128801
|
+
const parsed = JSON.parse(description.slice(jsonStart, i + 1));
|
|
128802
|
+
if (parsed && typeof parsed === "object" && parsed.repo && parsed.role) return parsed;
|
|
128803
|
+
} catch {
|
|
128804
|
+
return void 0;
|
|
128805
|
+
}
|
|
128806
|
+
return void 0;
|
|
128807
|
+
}
|
|
128808
|
+
}
|
|
128809
|
+
}
|
|
128810
|
+
return void 0;
|
|
128811
|
+
}
|
|
128812
|
+
|
|
128190
128813
|
// src/index.ts
|
|
128191
128814
|
function parseBooleanInput(value, defaultValue) {
|
|
128192
128815
|
const normalized = String(value || "").trim().toLowerCase();
|
|
@@ -128205,16 +128828,16 @@ function getInput2(name, env = process.env) {
|
|
|
128205
128828
|
const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
|
|
128206
128829
|
const hasNormalized = normalizedRaw !== void 0;
|
|
128207
128830
|
const hasRunner = runnerRaw !== void 0;
|
|
128831
|
+
const normalizedValue = normalizeInputValue(normalizedRaw);
|
|
128832
|
+
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128208
128833
|
if (hasNormalized && hasRunner) {
|
|
128209
|
-
|
|
128210
|
-
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128211
|
-
if (normalizedValue !== runnerValue) {
|
|
128834
|
+
if (normalizedValue && runnerValue && normalizedValue !== runnerValue) {
|
|
128212
128835
|
throw new Error(
|
|
128213
128836
|
`Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
|
|
128214
128837
|
);
|
|
128215
128838
|
}
|
|
128216
128839
|
}
|
|
128217
|
-
return
|
|
128840
|
+
return normalizedValue || runnerValue;
|
|
128218
128841
|
}
|
|
128219
128842
|
function hasInput(name, env = process.env) {
|
|
128220
128843
|
const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
|
|
@@ -128276,6 +128899,17 @@ function parseCredentialPreflight(value) {
|
|
|
128276
128899
|
`Unsupported credential-preflight "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128277
128900
|
);
|
|
128278
128901
|
}
|
|
128902
|
+
function parseBranchStrategy(value) {
|
|
128903
|
+
const definition = postmanRepoSyncActionContract.inputs["branch-strategy"];
|
|
128904
|
+
const allowed = definition.allowedValues ?? [];
|
|
128905
|
+
const normalized = String(value || "").trim() || (definition.default ?? "legacy");
|
|
128906
|
+
if (allowed.includes(normalized)) {
|
|
128907
|
+
return normalized;
|
|
128908
|
+
}
|
|
128909
|
+
throw new Error(
|
|
128910
|
+
`Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128911
|
+
);
|
|
128912
|
+
}
|
|
128279
128913
|
function normalizeReleaseLabel(value) {
|
|
128280
128914
|
const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128281
128915
|
return cleaned;
|
|
@@ -128320,6 +128954,7 @@ function resolveInputs(env = process.env) {
|
|
|
128320
128954
|
smokeCollectionId: getInput2("smoke-collection-id", env),
|
|
128321
128955
|
contractCollectionId: getInput2("contract-collection-id", env),
|
|
128322
128956
|
specId: getInput2("spec-id", env),
|
|
128957
|
+
specContentChanged: parseBooleanInput(getInput2("spec-content-changed", env), true),
|
|
128323
128958
|
specPath: getInput2("spec-path", env),
|
|
128324
128959
|
collectionSyncMode: normalizeCollectionSyncMode(getInput2("collection-sync-mode", env) || "refresh"),
|
|
128325
128960
|
specSyncMode: normalizeSpecSyncMode(getInput2("spec-sync-mode", env) || "update"),
|
|
@@ -128342,6 +128977,10 @@ function resolveInputs(env = process.env) {
|
|
|
128342
128977
|
postmanApiKey: getInput2("postman-api-key", env),
|
|
128343
128978
|
postmanAccessToken: getInput2("postman-access-token", env),
|
|
128344
128979
|
credentialPreflight: parseCredentialPreflight(getInput2("credential-preflight", env)),
|
|
128980
|
+
branchStrategy: parseBranchStrategy(getInput2("branch-strategy", env)),
|
|
128981
|
+
canonicalBranch: getInput2("canonical-branch", env) || void 0,
|
|
128982
|
+
channels: getInput2("channels", env) || void 0,
|
|
128983
|
+
previewTtlDays: Math.max(1, Number.parseInt(getInput2("preview-ttl", env) || "30", 10) || 30),
|
|
128345
128984
|
adoToken: getInput2("ado-token", env) || normalizeInputValue(env.SYSTEM_ACCESSTOKEN),
|
|
128346
128985
|
githubToken: getInput2("github-token", env),
|
|
128347
128986
|
ghFallbackToken: getInput2("gh-fallback-token", env),
|
|
@@ -128364,10 +129003,45 @@ function resolveInputs(env = process.env) {
|
|
|
128364
129003
|
postmanStack,
|
|
128365
129004
|
postmanApiBase: endpointProfile.apiBaseUrl,
|
|
128366
129005
|
postmanBifrostBase: endpointProfile.bifrostBaseUrl,
|
|
129006
|
+
postmanFallbackBase: endpointProfile.fallbackBaseUrl,
|
|
128367
129007
|
postmanCliInstallUrl: endpointProfile.cliInstallUrl,
|
|
128368
129008
|
postmanIapubBase: endpointProfile.iapubBaseUrl
|
|
128369
129009
|
};
|
|
128370
129010
|
}
|
|
129011
|
+
function buildBranchAssetMarker(decision, inputs, now = /* @__PURE__ */ new Date()) {
|
|
129012
|
+
if (decision.tier !== "preview" && decision.tier !== "channel") {
|
|
129013
|
+
return void 0;
|
|
129014
|
+
}
|
|
129015
|
+
const rawBranch = decision.identity.headBranch;
|
|
129016
|
+
const repo = inputs.repoUrl || inputs.repository;
|
|
129017
|
+
if (!rawBranch || !repo) {
|
|
129018
|
+
return void 0;
|
|
129019
|
+
}
|
|
129020
|
+
const ttlMs = inputs.previewTtlDays * 24 * 60 * 60 * 1e3;
|
|
129021
|
+
return {
|
|
129022
|
+
repo,
|
|
129023
|
+
rawBranch,
|
|
129024
|
+
sanitizedBranch: buildBranchSlug(rawBranch).suffix,
|
|
129025
|
+
role: decision.tier,
|
|
129026
|
+
headSha: decision.identity.headSha,
|
|
129027
|
+
createdAt: now.toISOString(),
|
|
129028
|
+
lastSyncedAt: now.toISOString(),
|
|
129029
|
+
...decision.tier === "preview" ? { expiresAt: new Date(now.getTime() + ttlMs).toISOString() } : {}
|
|
129030
|
+
};
|
|
129031
|
+
}
|
|
129032
|
+
function assertBranchAssetIds(inputs, decision, branchOwnedIds = process.env.POSTMAN_BRANCH_ASSET_IDS === "owned") {
|
|
129033
|
+
if (decision.tier === "legacy" || decision.tier === "canonical" || branchOwnedIds) return;
|
|
129034
|
+
const provided = [
|
|
129035
|
+
["baseline-collection-id", inputs.baselineCollectionId],
|
|
129036
|
+
["smoke-collection-id", inputs.smokeCollectionId],
|
|
129037
|
+
["contract-collection-id", inputs.contractCollectionId]
|
|
129038
|
+
].filter(([, value]) => Boolean(value));
|
|
129039
|
+
if (provided.length > 0) {
|
|
129040
|
+
throw new Error(
|
|
129041
|
+
`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.`
|
|
129042
|
+
);
|
|
129043
|
+
}
|
|
129044
|
+
}
|
|
128371
129045
|
function buildEnvironmentValues(envName, baseUrl) {
|
|
128372
129046
|
return [
|
|
128373
129047
|
{ key: "baseUrl", value: baseUrl, type: "default" },
|
|
@@ -128380,12 +129054,48 @@ function buildEnvironmentValues(envName, baseUrl) {
|
|
|
128380
129054
|
];
|
|
128381
129055
|
}
|
|
128382
129056
|
var LEGACY_BASELINE_COLLECTION_PREFIX = "[Baseline]";
|
|
129057
|
+
var RESOURCES_STATE_VERSION = 2;
|
|
129058
|
+
var SUPPORTED_STATE_VERSIONS = /* @__PURE__ */ new Set([1, RESOURCES_STATE_VERSION]);
|
|
129059
|
+
var StateUnreadableError = class extends Error {
|
|
129060
|
+
code = "CONTRACT_STATE_UNREADABLE";
|
|
129061
|
+
constructor(message) {
|
|
129062
|
+
super(`CONTRACT_STATE_UNREADABLE: ${message}`);
|
|
129063
|
+
this.name = "StateUnreadableError";
|
|
129064
|
+
}
|
|
129065
|
+
};
|
|
128383
129066
|
function readResourcesState() {
|
|
129067
|
+
let raw;
|
|
128384
129068
|
try {
|
|
128385
|
-
|
|
129069
|
+
raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
|
|
128386
129070
|
} catch {
|
|
128387
129071
|
return null;
|
|
128388
129072
|
}
|
|
129073
|
+
let parsed;
|
|
129074
|
+
try {
|
|
129075
|
+
parsed = load(raw);
|
|
129076
|
+
} catch (error2) {
|
|
129077
|
+
throw new StateUnreadableError(
|
|
129078
|
+
`.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.`
|
|
129079
|
+
);
|
|
129080
|
+
}
|
|
129081
|
+
if (parsed === null || parsed === void 0) {
|
|
129082
|
+
return null;
|
|
129083
|
+
}
|
|
129084
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
129085
|
+
throw new StateUnreadableError(
|
|
129086
|
+
".postman/resources.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to treat tracked state as absent."
|
|
129087
|
+
);
|
|
129088
|
+
}
|
|
129089
|
+
const state = parsed;
|
|
129090
|
+
if (state.version !== void 0 && !SUPPORTED_STATE_VERSIONS.has(Number(state.version))) {
|
|
129091
|
+
throw new StateUnreadableError(
|
|
129092
|
+
`.postman/resources.yaml declares unsupported state version ${String(state.version)} (supported: 1, ${RESOURCES_STATE_VERSION}). Upgrade the action or fix the file.`
|
|
129093
|
+
);
|
|
129094
|
+
}
|
|
129095
|
+
if (state.canonical && !state.cloudResources) {
|
|
129096
|
+
state.cloudResources = { ...state.canonical };
|
|
129097
|
+
}
|
|
129098
|
+
return state;
|
|
128389
129099
|
}
|
|
128390
129100
|
function findCloudResourceId(map, matcher) {
|
|
128391
129101
|
if (!map) {
|
|
@@ -128420,7 +129130,7 @@ function isOpenApiSpecFile(filePath) {
|
|
|
128420
129130
|
return false;
|
|
128421
129131
|
}
|
|
128422
129132
|
try {
|
|
128423
|
-
const raw = (0,
|
|
129133
|
+
const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
|
|
128424
129134
|
const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
|
|
128425
129135
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
128426
129136
|
return false;
|
|
@@ -128449,7 +129159,7 @@ function scanLocalSpecReferences(baseDir = ".") {
|
|
|
128449
129159
|
const found = /* @__PURE__ */ new Set();
|
|
128450
129160
|
const refs = [];
|
|
128451
129161
|
const visit2 = (currentDir) => {
|
|
128452
|
-
for (const entry of (0,
|
|
129162
|
+
for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
|
|
128453
129163
|
if (ignoredDirs.has(entry.name)) {
|
|
128454
129164
|
continue;
|
|
128455
129165
|
}
|
|
@@ -128479,7 +129189,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
|
|
|
128479
129189
|
const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
|
|
128480
129190
|
if (normalizedExplicitPath) {
|
|
128481
129191
|
const explicitFullPath = path8.resolve(normalizedExplicitPath);
|
|
128482
|
-
if ((0,
|
|
129192
|
+
if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
|
|
128483
129193
|
return {
|
|
128484
129194
|
repoRelativePath: normalizedExplicitPath,
|
|
128485
129195
|
configRelativePath: normalizeToPosix(path8.join("..", normalizedExplicitPath))
|
|
@@ -128506,7 +129216,11 @@ function createOutputs(inputs) {
|
|
|
128506
129216
|
"mock-url": "",
|
|
128507
129217
|
"monitor-id": "",
|
|
128508
129218
|
"repo-sync-summary-json": "{}",
|
|
128509
|
-
"commit-sha": ""
|
|
129219
|
+
"commit-sha": "",
|
|
129220
|
+
"sync-status": "",
|
|
129221
|
+
"branch-decision": "",
|
|
129222
|
+
"spec-version-tag": "",
|
|
129223
|
+
"spec-version-url": ""
|
|
128510
129224
|
};
|
|
128511
129225
|
}
|
|
128512
129226
|
function readActionInputs(actionCore) {
|
|
@@ -128551,6 +129265,10 @@ function readActionInputs(actionCore) {
|
|
|
128551
129265
|
INPUT_POSTMAN_API_KEY: postmanApiKey,
|
|
128552
129266
|
INPUT_POSTMAN_ACCESS_TOKEN: postmanAccessToken,
|
|
128553
129267
|
INPUT_CREDENTIAL_PREFLIGHT: readInput(actionCore, "credential-preflight") || "warn",
|
|
129268
|
+
INPUT_BRANCH_STRATEGY: readInput(actionCore, "branch-strategy"),
|
|
129269
|
+
INPUT_CANONICAL_BRANCH: readInput(actionCore, "canonical-branch"),
|
|
129270
|
+
INPUT_CHANNELS: readInput(actionCore, "channels"),
|
|
129271
|
+
INPUT_PREVIEW_TTL: readInput(actionCore, "preview-ttl"),
|
|
128554
129272
|
INPUT_ADO_TOKEN: adoToken,
|
|
128555
129273
|
INPUT_GITHUB_TOKEN: githubToken,
|
|
128556
129274
|
INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
|
|
@@ -128665,7 +129383,7 @@ async function persistSslSecrets(inputs, actionCore, actionExec, repository, env
|
|
|
128665
129383
|
);
|
|
128666
129384
|
}
|
|
128667
129385
|
}
|
|
128668
|
-
async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
129386
|
+
async function upsertEnvironments(inputs, dependencies, resourcesState, assetMarker) {
|
|
128669
129387
|
const envUids = {
|
|
128670
129388
|
...getEnvironmentUidsFromResources(resourcesState),
|
|
128671
129389
|
...inputs.environmentUids
|
|
@@ -128675,7 +129393,6 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128675
129393
|
}
|
|
128676
129394
|
for (const envName of inputs.environments) {
|
|
128677
129395
|
const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
|
|
128678
|
-
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
128679
129396
|
const displayName = `${inputs.projectName} - ${envName}`;
|
|
128680
129397
|
let existingUid = String(envUids[envName] || "").trim();
|
|
128681
129398
|
if (!existingUid) {
|
|
@@ -128691,10 +129408,27 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128691
129408
|
}
|
|
128692
129409
|
}
|
|
128693
129410
|
if (existingUid) {
|
|
128694
|
-
|
|
129411
|
+
let marker = assetMarker;
|
|
129412
|
+
if (marker) {
|
|
129413
|
+
try {
|
|
129414
|
+
const existing = await dependencies.postman.getEnvironment(existingUid);
|
|
129415
|
+
const values3 = existing.data?.values ?? existing.values ?? [];
|
|
129416
|
+
const prior = values3.find((value) => value.key === "x-pm-onboarding")?.value;
|
|
129417
|
+
const priorMarker = parseAssetMarker(prior ? `x-pm-onboarding: ${prior}` : void 0);
|
|
129418
|
+
if (priorMarker?.repo === marker.repo && priorMarker.rawBranch === marker.rawBranch) {
|
|
129419
|
+
marker = { ...marker, createdAt: priorMarker.createdAt };
|
|
129420
|
+
}
|
|
129421
|
+
} catch {
|
|
129422
|
+
}
|
|
129423
|
+
}
|
|
129424
|
+
const values2 = buildEnvironmentValues(envName, runtimeUrl);
|
|
129425
|
+
if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
|
|
129426
|
+
await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
|
|
128695
129427
|
envUids[envName] = existingUid;
|
|
128696
129428
|
continue;
|
|
128697
129429
|
}
|
|
129430
|
+
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
129431
|
+
if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
|
|
128698
129432
|
envUids[envName] = await dependencies.postman.createEnvironment(
|
|
128699
129433
|
inputs.workspaceId,
|
|
128700
129434
|
displayName,
|
|
@@ -128704,7 +129438,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128704
129438
|
return envUids;
|
|
128705
129439
|
}
|
|
128706
129440
|
function ensureDir(path9) {
|
|
128707
|
-
(0,
|
|
129441
|
+
(0, import_node_fs4.mkdirSync)(path9, { recursive: true });
|
|
128708
129442
|
}
|
|
128709
129443
|
function getCollectionDirectoryName(kind, projectName) {
|
|
128710
129444
|
if (kind === "Baseline") {
|
|
@@ -128741,7 +129475,7 @@ function stripVolatileFields(obj) {
|
|
|
128741
129475
|
}
|
|
128742
129476
|
function writeJsonFile(path9, content, normalize3 = false) {
|
|
128743
129477
|
const data = normalize3 ? stripVolatileFields(content) : content;
|
|
128744
|
-
(0,
|
|
129478
|
+
(0, import_node_fs4.writeFileSync)(path9, JSON.stringify(data, null, 2));
|
|
128745
129479
|
}
|
|
128746
129480
|
function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
|
|
128747
129481
|
if (specSyncMode !== "version") {
|
|
@@ -128765,31 +129499,30 @@ function resolveDurableWorkspaceId(options) {
|
|
|
128765
129499
|
}
|
|
128766
129500
|
return prior === candidate ? prior : void 0;
|
|
128767
129501
|
}
|
|
128768
|
-
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs) {
|
|
128769
|
-
const manifest = {};
|
|
129502
|
+
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs, priorState) {
|
|
129503
|
+
const manifest = { ...priorState ?? {} };
|
|
129504
|
+
delete manifest.version;
|
|
129505
|
+
delete manifest.workspace;
|
|
129506
|
+
delete manifest.localResources;
|
|
129507
|
+
delete manifest.cloudResources;
|
|
129508
|
+
delete manifest.canonical;
|
|
129509
|
+
manifest.version = RESOURCES_STATE_VERSION;
|
|
128770
129510
|
if (workspaceId) {
|
|
128771
129511
|
manifest.workspace = { id: workspaceId };
|
|
128772
129512
|
}
|
|
128773
|
-
const localResources = {};
|
|
128774
129513
|
const cloudResources = {};
|
|
128775
129514
|
const collectionKeys = Object.keys(collectionMap);
|
|
128776
129515
|
if (collectionKeys.length > 0) {
|
|
128777
|
-
localResources.collections = collectionKeys;
|
|
128778
129516
|
cloudResources.collections = collectionMap;
|
|
128779
129517
|
}
|
|
128780
129518
|
const envEntries = Object.entries(envMap);
|
|
128781
129519
|
if (envEntries.length > 0) {
|
|
128782
|
-
localResources.environments = envEntries.map(
|
|
128783
|
-
([envName]) => `../${artifactDir}/environments/${envName}.postman_environment.json`
|
|
128784
|
-
);
|
|
128785
129520
|
cloudResources.environments = {};
|
|
128786
129521
|
for (const [envName, envUid] of envEntries) {
|
|
128787
129522
|
cloudResources.environments[`../${artifactDir}/environments/${envName}.postman_environment.json`] = envUid;
|
|
128788
129523
|
}
|
|
128789
129524
|
}
|
|
128790
|
-
|
|
128791
|
-
localResources.specs = localSpecRefs;
|
|
128792
|
-
}
|
|
129525
|
+
void localSpecRefs;
|
|
128793
129526
|
const specs = { ...existingSpecs || {} };
|
|
128794
129527
|
if (mappedSpecRef && specId) {
|
|
128795
129528
|
specs[mappedSpecRef] = specId;
|
|
@@ -128797,11 +129530,8 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
|
|
|
128797
129530
|
if (Object.keys(specs).length > 0) {
|
|
128798
129531
|
cloudResources.specs = specs;
|
|
128799
129532
|
}
|
|
128800
|
-
if (Object.keys(localResources).length > 0) {
|
|
128801
|
-
manifest.localResources = localResources;
|
|
128802
|
-
}
|
|
128803
129533
|
if (Object.keys(cloudResources).length > 0) {
|
|
128804
|
-
manifest.
|
|
129534
|
+
manifest.canonical = cloudResources;
|
|
128805
129535
|
}
|
|
128806
129536
|
return dump(manifest, {
|
|
128807
129537
|
lineWidth: -1,
|
|
@@ -128842,21 +129572,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
|
|
|
128842
129572
|
if (!rawPath || hasControlCharacter2(originalPath) || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
|
|
128843
129573
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128844
129574
|
}
|
|
128845
|
-
const base = (0,
|
|
129575
|
+
const base = (0, import_node_fs4.realpathSync)(process.cwd());
|
|
128846
129576
|
const resolved = path8.resolve(base, rawPath);
|
|
128847
129577
|
const relative4 = path8.relative(base, resolved);
|
|
128848
129578
|
if (relative4.startsWith("..") || path8.isAbsolute(relative4)) {
|
|
128849
129579
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128850
129580
|
}
|
|
128851
129581
|
let existingPath = resolved;
|
|
128852
|
-
while (!(0,
|
|
129582
|
+
while (!(0, import_node_fs4.existsSync)(existingPath)) {
|
|
128853
129583
|
const parent = path8.dirname(existingPath);
|
|
128854
129584
|
if (parent === existingPath) {
|
|
128855
129585
|
break;
|
|
128856
129586
|
}
|
|
128857
129587
|
existingPath = parent;
|
|
128858
129588
|
}
|
|
128859
|
-
const realExistingPath = (0,
|
|
129589
|
+
const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
|
|
128860
129590
|
const realRelative = path8.relative(base, realExistingPath);
|
|
128861
129591
|
if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
|
|
128862
129592
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
@@ -128884,8 +129614,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128884
129614
|
ensureDir(specsDir);
|
|
128885
129615
|
ensureDir(".postman");
|
|
128886
129616
|
const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
|
|
128887
|
-
if (!(0,
|
|
128888
|
-
(0,
|
|
129617
|
+
if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
|
|
129618
|
+
(0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
|
|
128889
129619
|
}
|
|
128890
129620
|
if (inputs.generateCiWorkflow) {
|
|
128891
129621
|
const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
|
|
@@ -128932,7 +129662,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128932
129662
|
workspaceLinkEnabled: inputs.workspaceLinkEnabled,
|
|
128933
129663
|
workspaceLinkStatus: options.workspaceLinkStatus
|
|
128934
129664
|
});
|
|
128935
|
-
(0,
|
|
129665
|
+
(0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
128936
129666
|
durableWorkspaceId,
|
|
128937
129667
|
manifestCollections,
|
|
128938
129668
|
envUids,
|
|
@@ -128940,10 +129670,11 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128940
129670
|
discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
128941
129671
|
mappedSpecCloudKey,
|
|
128942
129672
|
inputs.specId || void 0,
|
|
128943
|
-
options.existingSpecs
|
|
129673
|
+
options.existingSpecs,
|
|
129674
|
+
options.priorState
|
|
128944
129675
|
));
|
|
128945
129676
|
if (mappedSpec && Object.keys(manifestCollections).length > 0) {
|
|
128946
|
-
(0,
|
|
129677
|
+
(0, import_node_fs4.writeFileSync)(
|
|
128947
129678
|
".postman/workflows.yaml",
|
|
128948
129679
|
buildSpecCollectionWorkflowManifest(
|
|
128949
129680
|
mappedSpec.configRelativePath,
|
|
@@ -128988,19 +129719,29 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
|
|
|
128988
129719
|
const dir = parts.slice(0, -1).join("/");
|
|
128989
129720
|
ensureDir(dir);
|
|
128990
129721
|
}
|
|
128991
|
-
(0,
|
|
129722
|
+
(0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
|
|
129723
|
+
if (inputs.provider === "github" || inputs.provider === "unknown") {
|
|
129724
|
+
const gcPath = ".github/workflows/postman-preview-gc.yml";
|
|
129725
|
+
if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
|
|
129726
|
+
ensureDir(".github/workflows");
|
|
129727
|
+
(0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
|
|
129728
|
+
}
|
|
129729
|
+
}
|
|
128992
129730
|
}
|
|
128993
129731
|
if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
|
|
128994
129732
|
return { commitSha: "", resolvedCurrentRef: "", pushed: false };
|
|
128995
129733
|
}
|
|
128996
129734
|
const provisionPath = ".github/workflows/provision.yml";
|
|
128997
|
-
const provisionExists = inputs.provider === "github" && (0,
|
|
129735
|
+
const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
|
|
129736
|
+
const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
|
|
129737
|
+
const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
|
|
128998
129738
|
const stagePaths = [
|
|
128999
129739
|
inputs.artifactDir,
|
|
129000
129740
|
".postman",
|
|
129001
129741
|
inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
|
|
129742
|
+
gcExists ? gcWorkflowPath : null,
|
|
129002
129743
|
provisionExists ? provisionPath : null
|
|
129003
|
-
].filter((entry) => typeof entry === "string" && ((0,
|
|
129744
|
+
].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
|
|
129004
129745
|
if (stagePaths.length === 0) {
|
|
129005
129746
|
dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
|
|
129006
129747
|
return {
|
|
@@ -129048,6 +129789,31 @@ async function runRepoSync(inputs, dependencies) {
|
|
|
129048
129789
|
}
|
|
129049
129790
|
}
|
|
129050
129791
|
async function runRepoSyncInner(inputs, dependencies) {
|
|
129792
|
+
const branchDecision = decideBranchTier(inputs);
|
|
129793
|
+
assertBranchAssetIds(inputs, branchDecision);
|
|
129794
|
+
const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
|
|
129795
|
+
if (!isCanonicalWriter) {
|
|
129796
|
+
if (branchDecision.tier === "preview" && branchDecision.identity.headBranch) {
|
|
129797
|
+
inputs = {
|
|
129798
|
+
...inputs,
|
|
129799
|
+
projectName: previewAssetName(inputs.projectName, branchDecision.identity.headBranch),
|
|
129800
|
+
workspaceLinkEnabled: false,
|
|
129801
|
+
environmentSyncEnabled: false,
|
|
129802
|
+
repoWriteMode: "none"
|
|
129803
|
+
};
|
|
129804
|
+
} else if (branchDecision.tier === "channel" && branchDecision.channel) {
|
|
129805
|
+
inputs = {
|
|
129806
|
+
...inputs,
|
|
129807
|
+
projectName: channelAssetName(inputs.projectName, branchDecision.channel.code),
|
|
129808
|
+
workspaceLinkEnabled: false,
|
|
129809
|
+
environmentSyncEnabled: false,
|
|
129810
|
+
repoWriteMode: "none"
|
|
129811
|
+
};
|
|
129812
|
+
}
|
|
129813
|
+
dependencies.core.info(
|
|
129814
|
+
`branch-aware sync: ${branchDecision.tier} run \u2014 branch-scoped asset set "${inputs.projectName}", no workspace repo-link mutation, no state write-back`
|
|
129815
|
+
);
|
|
129816
|
+
}
|
|
129051
129817
|
const outputs = createOutputs(inputs);
|
|
129052
129818
|
const versionRequested = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
|
|
129053
129819
|
const releaseLabel = deriveReleaseLabel(inputs);
|
|
@@ -129055,7 +129821,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129055
129821
|
throw new Error("release-label is required when collection-sync-mode or spec-sync-mode is version");
|
|
129056
129822
|
}
|
|
129057
129823
|
const assetProjectName = createAssetProjectName(inputs, releaseLabel);
|
|
129058
|
-
const
|
|
129824
|
+
const trackedState = readResourcesState();
|
|
129825
|
+
const resourcesState = isCanonicalWriter ? trackedState : trackedState?.workspace ? { workspace: trackedState.workspace } : null;
|
|
129059
129826
|
if (resourcesState) {
|
|
129060
129827
|
if (!inputs.workspaceId && resourcesState.workspace?.id) {
|
|
129061
129828
|
inputs.workspaceId = resourcesState.workspace.id;
|
|
@@ -129081,7 +129848,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129081
129848
|
}
|
|
129082
129849
|
}
|
|
129083
129850
|
}
|
|
129084
|
-
const
|
|
129851
|
+
const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
|
|
129852
|
+
const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
|
|
129085
129853
|
outputs["environment-uids-json"] = JSON.stringify(envUids);
|
|
129086
129854
|
if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
|
|
129087
129855
|
const associations = Object.entries(envUids).map(([envName, envUid]) => ({
|
|
@@ -129137,11 +129905,23 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129137
129905
|
}
|
|
129138
129906
|
}
|
|
129139
129907
|
if (!resolvedMockUrl) {
|
|
129140
|
-
const mock = await
|
|
129141
|
-
|
|
129142
|
-
|
|
129143
|
-
|
|
129144
|
-
|
|
129908
|
+
const mock = await retry(
|
|
129909
|
+
() => dependencies.postman.createMock(
|
|
129910
|
+
inputs.workspaceId,
|
|
129911
|
+
mockName,
|
|
129912
|
+
inputs.baselineCollectionId,
|
|
129913
|
+
mockEnvUid
|
|
129914
|
+
),
|
|
129915
|
+
{
|
|
129916
|
+
maxAttempts: 3,
|
|
129917
|
+
delayMs: 2e3,
|
|
129918
|
+
backoffMultiplier: 2,
|
|
129919
|
+
onRetry: ({ attempt, error: error2 }) => {
|
|
129920
|
+
dependencies.core.warning(
|
|
129921
|
+
`Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
|
|
129922
|
+
);
|
|
129923
|
+
}
|
|
129924
|
+
}
|
|
129145
129925
|
);
|
|
129146
129926
|
resolvedMockUrl = mock.url;
|
|
129147
129927
|
dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
|
|
@@ -129210,16 +129990,19 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129210
129990
|
);
|
|
129211
129991
|
} catch (error2) {
|
|
129212
129992
|
outputs["workspace-link-status"] = "failed";
|
|
129213
|
-
|
|
129214
|
-
|
|
129215
|
-
|
|
129993
|
+
const message = `Workspace link failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
129994
|
+
if (branchDecision.tier === "canonical") {
|
|
129995
|
+
throw new Error(message, { cause: error2 });
|
|
129996
|
+
}
|
|
129997
|
+
dependencies.core.warning(message);
|
|
129216
129998
|
}
|
|
129217
129999
|
}
|
|
129218
130000
|
await exportArtifacts(inputs, dependencies, envUids, assetProjectName, {
|
|
129219
130001
|
workspaceLinkStatus: outputs["workspace-link-status"],
|
|
129220
130002
|
priorWorkspaceId: resourcesState?.workspace?.id,
|
|
129221
130003
|
existingSpecs: resourcesState?.cloudResources?.specs,
|
|
129222
|
-
releaseLabel
|
|
130004
|
+
releaseLabel,
|
|
130005
|
+
priorState: resourcesState
|
|
129223
130006
|
});
|
|
129224
130007
|
const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
|
|
129225
130008
|
outputs["commit-sha"] = commit.commitSha;
|
|
@@ -129227,6 +130010,38 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129227
130010
|
outputs["resolved-current-ref"] = commit.resolvedCurrentRef;
|
|
129228
130011
|
}
|
|
129229
130012
|
outputs["repo-sync-summary-json"] = createRepoSummary(outputs, envUids, commit.pushed);
|
|
130013
|
+
if (branchDecision.tier === "canonical" && inputs.specId && inputs.specContentChanged !== false && dependencies.postman.tagSpecVersion) {
|
|
130014
|
+
const shortSha = (branchDecision.identity.headSha ?? "").slice(0, 7);
|
|
130015
|
+
const tagName = inputs.releaseLabel ? `${inputs.releaseLabel}${shortSha ? ` (${shortSha})` : ""}` : shortSha || `sync-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
130016
|
+
try {
|
|
130017
|
+
const tag = await dependencies.postman.tagSpecVersion(inputs.specId, tagName);
|
|
130018
|
+
outputs["spec-version-tag"] = tag.name || tagName;
|
|
130019
|
+
if (tag.id) {
|
|
130020
|
+
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)}`;
|
|
130021
|
+
}
|
|
130022
|
+
dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
|
|
130023
|
+
} catch (error2) {
|
|
130024
|
+
const status = error2.status;
|
|
130025
|
+
if (status === 409) {
|
|
130026
|
+
const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
|
|
130027
|
+
const latest = tags[0];
|
|
130028
|
+
if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
|
|
130029
|
+
outputs["spec-version-tag"] = latest.name;
|
|
130030
|
+
outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
|
|
130031
|
+
dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
|
|
130032
|
+
} else {
|
|
130033
|
+
dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
|
|
130034
|
+
}
|
|
130035
|
+
} else {
|
|
130036
|
+
dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
130037
|
+
}
|
|
130038
|
+
}
|
|
130039
|
+
}
|
|
130040
|
+
if (inputs.branchStrategy !== "legacy" || process.env[BRANCH_DECISION_ENV]) {
|
|
130041
|
+
const decision = decideBranchTier(inputs);
|
|
130042
|
+
outputs["sync-status"] = "synced";
|
|
130043
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
130044
|
+
}
|
|
129230
130045
|
for (const [name, value] of Object.entries(outputs)) {
|
|
129231
130046
|
dependencies.core.setOutput(name, value);
|
|
129232
130047
|
}
|
|
@@ -129330,6 +130145,8 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
|
|
|
129330
130145
|
const gateway = new AccessTokenGatewayClient({
|
|
129331
130146
|
tokenProvider,
|
|
129332
130147
|
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
130148
|
+
// No fallback on the org-mode probe: the expected non-org 400 must
|
|
130149
|
+
// surface verbatim, not be re-fired against the /_api alias.
|
|
129333
130150
|
secretMasker: masker
|
|
129334
130151
|
});
|
|
129335
130152
|
const squads = await gateway.getSquads(teamId);
|
|
@@ -129385,6 +130202,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129385
130202
|
const gateway = new AccessTokenGatewayClient({
|
|
129386
130203
|
tokenProvider,
|
|
129387
130204
|
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
130205
|
+
fallbackBaseUrl: inputs.postmanFallbackBase,
|
|
129388
130206
|
teamId: resolved.teamId,
|
|
129389
130207
|
orgMode: inputs.orgMode,
|
|
129390
130208
|
secretMasker: masker
|
|
@@ -129416,7 +130234,19 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129416
130234
|
listMonitors: gatewayAssets.listMonitors.bind(gatewayAssets),
|
|
129417
130235
|
monitorExists: gatewayAssets.monitorExists.bind(gatewayAssets),
|
|
129418
130236
|
findMonitorByCollection: gatewayAssets.findMonitorByCollection.bind(gatewayAssets),
|
|
129419
|
-
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets)
|
|
130237
|
+
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets),
|
|
130238
|
+
listEnvironments: gatewayAssets.listEnvironments.bind(gatewayAssets),
|
|
130239
|
+
// GC path (preview/channel retention machine — lib/repo/preview-gc.ts).
|
|
130240
|
+
deleteEnvironment: gatewayAssets.deleteEnvironment.bind(gatewayAssets),
|
|
130241
|
+
deleteMock: gatewayAssets.deleteMock.bind(gatewayAssets),
|
|
130242
|
+
deleteMonitor: gatewayAssets.deleteMonitor.bind(gatewayAssets),
|
|
130243
|
+
deleteCollection: gatewayAssets.deleteCollection.bind(gatewayAssets),
|
|
130244
|
+
listSpecifications: gatewayAssets.listSpecifications.bind(gatewayAssets),
|
|
130245
|
+
getSpecContent: gatewayAssets.getSpecContent.bind(gatewayAssets),
|
|
130246
|
+
listSpecCollections: gatewayAssets.listSpecCollections.bind(gatewayAssets),
|
|
130247
|
+
deleteSpec: gatewayAssets.deleteSpec.bind(gatewayAssets),
|
|
130248
|
+
tagSpecVersion: gatewayAssets.tagSpecVersion.bind(gatewayAssets),
|
|
130249
|
+
listSpecVersionTags: gatewayAssets.listSpecVersionTags.bind(gatewayAssets)
|
|
129420
130250
|
};
|
|
129421
130251
|
const repoMutation = repository && (inputs.repoWriteMode === "commit-only" || inputs.repoWriteMode === "commit-and-push") ? new RepoMutationService({
|
|
129422
130252
|
provider: inputs.provider,
|
|
@@ -129453,8 +130283,43 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129453
130283
|
repoMutation
|
|
129454
130284
|
};
|
|
129455
130285
|
}
|
|
130286
|
+
function decideBranchTier(inputs, env = process.env) {
|
|
130287
|
+
return resolveEffectiveBranchDecision(
|
|
130288
|
+
{
|
|
130289
|
+
strategy: inputs.branchStrategy,
|
|
130290
|
+
identity: resolveBranchIdentity(env, { defaultBranch: inputs.canonicalBranch }),
|
|
130291
|
+
canonicalBranch: inputs.canonicalBranch,
|
|
130292
|
+
channels: parseChannelRules(inputs.channels)
|
|
130293
|
+
},
|
|
130294
|
+
env
|
|
130295
|
+
);
|
|
130296
|
+
}
|
|
130297
|
+
function runGatedSkip(inputs, decision, actionCore) {
|
|
130298
|
+
actionCore.info(
|
|
130299
|
+
`branch-aware sync: gated run (${decision.reason}) \u2014 repo-sync skipped, zero workspace writes`
|
|
130300
|
+
);
|
|
130301
|
+
const outputs = createOutputs(inputs);
|
|
130302
|
+
outputs["sync-status"] = "skipped-branch-gate";
|
|
130303
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
130304
|
+
outputs["repo-sync-summary-json"] = JSON.stringify({
|
|
130305
|
+
status: "skipped-branch-gate",
|
|
130306
|
+
reason: decision.reason
|
|
130307
|
+
});
|
|
130308
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
130309
|
+
actionCore.setOutput(name, value);
|
|
130310
|
+
}
|
|
130311
|
+
return outputs;
|
|
130312
|
+
}
|
|
129456
130313
|
async function runAction(actionCore = core_exports, actionExec = exec_exports) {
|
|
129457
130314
|
const inputs = readActionInputs(actionCore);
|
|
130315
|
+
const branchDecision = decideBranchTier(inputs);
|
|
130316
|
+
if (branchDecision.tier === "gated") {
|
|
130317
|
+
return runGatedSkip(inputs, branchDecision, actionCore);
|
|
130318
|
+
}
|
|
130319
|
+
if (branchDecision.tier !== "legacy") {
|
|
130320
|
+
actionCore.info(`branch-aware sync: tier=${branchDecision.tier} (${branchDecision.reason})`);
|
|
130321
|
+
process.env[BRANCH_DECISION_ENV] = serializeBranchDecision(branchDecision);
|
|
130322
|
+
}
|
|
129458
130323
|
await mintAccessTokenIfNeeded(inputs, {
|
|
129459
130324
|
info: (message) => actionCore.info(message),
|
|
129460
130325
|
warning: (message) => actionCore.warning(message)
|