@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/index.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,
|
|
@@ -119787,14 +119787,19 @@ var require_node2 = __commonJS({
|
|
|
119787
119787
|
// src/index.ts
|
|
119788
119788
|
var index_exports = {};
|
|
119789
119789
|
__export(index_exports, {
|
|
119790
|
+
StateUnreadableError: () => StateUnreadableError,
|
|
119791
|
+
assertBranchAssetIds: () => assertBranchAssetIds,
|
|
119790
119792
|
assertPathWithinCwd: () => assertPathWithinCwd,
|
|
119793
|
+
buildBranchAssetMarker: () => buildBranchAssetMarker,
|
|
119791
119794
|
createRepoSyncDependencies: () => createRepoSyncDependencies,
|
|
119795
|
+
decideBranchTier: () => decideBranchTier,
|
|
119792
119796
|
getInput: () => getInput2,
|
|
119793
119797
|
hasInput: () => hasInput,
|
|
119794
119798
|
readActionInputs: () => readActionInputs,
|
|
119795
119799
|
resolveInputs: () => resolveInputs,
|
|
119796
119800
|
resolvePostmanApiKeyAndTeamId: () => resolvePostmanApiKeyAndTeamId,
|
|
119797
119801
|
runAction: () => runAction,
|
|
119802
|
+
runGatedSkip: () => runGatedSkip,
|
|
119798
119803
|
runRepoSync: () => runRepoSync
|
|
119799
119804
|
});
|
|
119800
119805
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -122078,7 +122083,7 @@ function getIDToken(aud) {
|
|
|
122078
122083
|
}
|
|
122079
122084
|
|
|
122080
122085
|
// src/index.ts
|
|
122081
|
-
var
|
|
122086
|
+
var import_node_fs4 = require("node:fs");
|
|
122082
122087
|
var path8 = __toESM(require("node:path"), 1);
|
|
122083
122088
|
|
|
122084
122089
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -125129,12 +125134,14 @@ var POSTMAN_ENDPOINT_PROFILES = {
|
|
|
125129
125134
|
prod: {
|
|
125130
125135
|
apiBaseUrl: "https://api.getpostman.com",
|
|
125131
125136
|
bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
|
|
125137
|
+
fallbackBaseUrl: "https://go.postman.co/_api",
|
|
125132
125138
|
cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
|
|
125133
125139
|
iapubBaseUrl: "https://iapub.postman.co"
|
|
125134
125140
|
},
|
|
125135
125141
|
beta: {
|
|
125136
125142
|
apiBaseUrl: "https://api.getpostman-beta.com",
|
|
125137
125143
|
bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
|
|
125144
|
+
fallbackBaseUrl: "https://go.postman-beta.co/_api",
|
|
125138
125145
|
cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
|
|
125139
125146
|
iapubBaseUrl: "https://iapub.postman.co"
|
|
125140
125147
|
}
|
|
@@ -125201,6 +125208,9 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125201
125208
|
" branches: [main]",
|
|
125202
125209
|
" schedule:",
|
|
125203
125210
|
' - cron: "0 */6 * * *"',
|
|
125211
|
+
"concurrency:",
|
|
125212
|
+
" group: postman-onboard-${{ github.head_ref || github.ref_name }}",
|
|
125213
|
+
" cancel-in-progress: false",
|
|
125204
125214
|
"jobs:",
|
|
125205
125215
|
" test:",
|
|
125206
125216
|
" runs-on: ubuntu-latest",
|
|
@@ -125217,7 +125227,7 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125217
125227
|
" ruby <<'RUBY'",
|
|
125218
125228
|
" require 'yaml'",
|
|
125219
125229
|
" config = YAML.load_file('.postman/resources.yaml') || {}",
|
|
125220
|
-
" cloud = config.fetch('cloudResources', {})",
|
|
125230
|
+
" cloud = config.fetch('canonical', config.fetch('cloudResources', {}))",
|
|
125221
125231
|
" collections = cloud.fetch('collections', {})",
|
|
125222
125232
|
" environments = cloud.fetch('environments', {})",
|
|
125223
125233
|
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
@@ -125415,6 +125425,58 @@ function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
|
|
|
125415
125425
|
""
|
|
125416
125426
|
];
|
|
125417
125427
|
}
|
|
125428
|
+
function renderGcWorkflowTemplate() {
|
|
125429
|
+
return [
|
|
125430
|
+
"name: Postman Preview GC",
|
|
125431
|
+
"on:",
|
|
125432
|
+
" delete:",
|
|
125433
|
+
' branches: ["**"]',
|
|
125434
|
+
" pull_request:",
|
|
125435
|
+
" types: [closed]",
|
|
125436
|
+
" schedule:",
|
|
125437
|
+
' - cron: "0 2 * * *"',
|
|
125438
|
+
" workflow_dispatch:",
|
|
125439
|
+
" inputs:",
|
|
125440
|
+
" branch:",
|
|
125441
|
+
" description: Branch name to GC (optional, otherwise sweep by TTL/branch-existence)",
|
|
125442
|
+
" required: false",
|
|
125443
|
+
"concurrency:",
|
|
125444
|
+
" group: postman-preview-gc-${{ github.ref_name }}",
|
|
125445
|
+
" cancel-in-progress: false",
|
|
125446
|
+
"jobs:",
|
|
125447
|
+
" gc:",
|
|
125448
|
+
" runs-on: ubuntu-latest",
|
|
125449
|
+
" if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
125450
|
+
" steps:",
|
|
125451
|
+
" - uses: actions/checkout@v5",
|
|
125452
|
+
" with:",
|
|
125453
|
+
" fetch-depth: 0",
|
|
125454
|
+
" - name: Run Postman preview GC (provider-neutral cli.cjs gc)",
|
|
125455
|
+
" env:",
|
|
125456
|
+
" POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}",
|
|
125457
|
+
" POSTMAN_ACCESS_TOKEN: ${{ secrets.POSTMAN_ACCESS_TOKEN }}",
|
|
125458
|
+
" POSTMAN_WORKSPACE_ID: ${{ vars.POSTMAN_WORKSPACE_ID }}",
|
|
125459
|
+
" REPO_URL: https://github.com/${{ github.repository }}",
|
|
125460
|
+
" run: |",
|
|
125461
|
+
" set -euo pipefail",
|
|
125462
|
+
" # The repo-sync action bundles a provider-neutral gc command (cli.cjs gc)",
|
|
125463
|
+
" # that uses the Postman access token for inventory/deletion and the",
|
|
125464
|
+
" # provider ambient credential (GITHUB_TOKEN via git ls-remote) for branch existence.",
|
|
125465
|
+
" # Daily scheduled run is the retention executor (TTL contract of last resort).",
|
|
125466
|
+
' if [ -n "${{ inputs.branch }}" ]; then',
|
|
125467
|
+
' npx @postman-cse/onboarding-repo-sync gc --branch "${{ inputs.branch }}" --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125468
|
+
" else",
|
|
125469
|
+
' npx @postman-cse/onboarding-repo-sync gc --workspace-id "$POSTMAN_WORKSPACE_ID" --repo-url "$REPO_URL"',
|
|
125470
|
+
" fi",
|
|
125471
|
+
" - name: Orphan audit summary (job summary)",
|
|
125472
|
+
" if: always()",
|
|
125473
|
+
" run: |",
|
|
125474
|
+
' echo "### Preview GC orphan audit" >> "$GITHUB_STEP_SUMMARY"',
|
|
125475
|
+
' echo "Marker-guarded deletion only \u2014 strangers (no marker) are never deleted. See gc-summary-json for structured counts." >> "$GITHUB_STEP_SUMMARY"',
|
|
125476
|
+
""
|
|
125477
|
+
].join("\n");
|
|
125478
|
+
}
|
|
125479
|
+
var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
|
|
125418
125480
|
function getCiWorkflowTemplate(provider, options = {}) {
|
|
125419
125481
|
if (provider === "azure-devops") {
|
|
125420
125482
|
const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
|
|
@@ -126985,6 +127047,53 @@ function createInternalIntegrationAdapter(options) {
|
|
|
126985
127047
|
return new BifrostInternalIntegrationAdapter(options);
|
|
126986
127048
|
}
|
|
126987
127049
|
|
|
127050
|
+
// src/lib/retry.ts
|
|
127051
|
+
function sleep(delayMs) {
|
|
127052
|
+
return new Promise((resolve3) => {
|
|
127053
|
+
setTimeout(resolve3, delayMs);
|
|
127054
|
+
});
|
|
127055
|
+
}
|
|
127056
|
+
function normalizeRetryOptions(options) {
|
|
127057
|
+
return {
|
|
127058
|
+
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
127059
|
+
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
127060
|
+
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
127061
|
+
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
127062
|
+
onRetry: options.onRetry ?? (async () => void 0),
|
|
127063
|
+
shouldRetry: options.shouldRetry ?? (() => true),
|
|
127064
|
+
sleep: options.sleep ?? sleep
|
|
127065
|
+
};
|
|
127066
|
+
}
|
|
127067
|
+
async function retry(operation, options = {}) {
|
|
127068
|
+
const normalized = normalizeRetryOptions(options);
|
|
127069
|
+
let nextDelayMs = normalized.delayMs;
|
|
127070
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
127071
|
+
try {
|
|
127072
|
+
return await operation();
|
|
127073
|
+
} catch (error2) {
|
|
127074
|
+
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
127075
|
+
attempt,
|
|
127076
|
+
maxAttempts: normalized.maxAttempts
|
|
127077
|
+
});
|
|
127078
|
+
if (!shouldRetry) {
|
|
127079
|
+
throw error2;
|
|
127080
|
+
}
|
|
127081
|
+
await normalized.onRetry({
|
|
127082
|
+
attempt,
|
|
127083
|
+
maxAttempts: normalized.maxAttempts,
|
|
127084
|
+
delayMs: nextDelayMs,
|
|
127085
|
+
error: error2
|
|
127086
|
+
});
|
|
127087
|
+
await normalized.sleep(nextDelayMs);
|
|
127088
|
+
nextDelayMs = Math.min(
|
|
127089
|
+
normalized.maxDelayMs,
|
|
127090
|
+
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
127091
|
+
);
|
|
127092
|
+
}
|
|
127093
|
+
}
|
|
127094
|
+
throw new Error("Retry exhausted without returning or throwing");
|
|
127095
|
+
}
|
|
127096
|
+
|
|
126988
127097
|
// src/contracts.ts
|
|
126989
127098
|
var postmanRepoSyncActionContract = {
|
|
126990
127099
|
name: "postman-repo-sync-action",
|
|
@@ -127154,6 +127263,25 @@ var postmanRepoSyncActionContract = {
|
|
|
127154
127263
|
default: "warn",
|
|
127155
127264
|
allowedValues: ["enforce", "warn"]
|
|
127156
127265
|
},
|
|
127266
|
+
"branch-strategy": {
|
|
127267
|
+
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.",
|
|
127268
|
+
required: false,
|
|
127269
|
+
default: "legacy",
|
|
127270
|
+
allowedValues: ["legacy", "preview", "publish-gate"]
|
|
127271
|
+
},
|
|
127272
|
+
"canonical-branch": {
|
|
127273
|
+
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.",
|
|
127274
|
+
required: false
|
|
127275
|
+
},
|
|
127276
|
+
"channels": {
|
|
127277
|
+
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.',
|
|
127278
|
+
required: false
|
|
127279
|
+
},
|
|
127280
|
+
"preview-ttl": {
|
|
127281
|
+
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).",
|
|
127282
|
+
required: false,
|
|
127283
|
+
default: "30"
|
|
127284
|
+
},
|
|
127157
127285
|
"github-token": {
|
|
127158
127286
|
description: "GitHub token used for repo variable persistence and commits.",
|
|
127159
127287
|
required: false
|
|
@@ -127191,6 +127319,11 @@ var postmanRepoSyncActionContract = {
|
|
|
127191
127319
|
description: "Spec UID from bootstrap, persisted into .postman/resources.yaml cloudResources.",
|
|
127192
127320
|
required: false
|
|
127193
127321
|
},
|
|
127322
|
+
"spec-content-changed": {
|
|
127323
|
+
description: "Whether bootstrap changed canonical spec content; controls final native Spec Hub tag publication.",
|
|
127324
|
+
required: false,
|
|
127325
|
+
default: "true"
|
|
127326
|
+
},
|
|
127194
127327
|
"spec-path": {
|
|
127195
127328
|
description: "Optional repo-root-relative path to the local spec file for resources/workflows metadata.",
|
|
127196
127329
|
required: false
|
|
@@ -127235,6 +127368,18 @@ var postmanRepoSyncActionContract = {
|
|
|
127235
127368
|
},
|
|
127236
127369
|
"commit-sha": {
|
|
127237
127370
|
description: "Commit SHA produced by repo-write-mode, if any."
|
|
127371
|
+
},
|
|
127372
|
+
"sync-status": {
|
|
127373
|
+
description: "Branch-aware sync status: synced, skipped-branch-gate, or empty under branch-strategy legacy."
|
|
127374
|
+
},
|
|
127375
|
+
"branch-decision": {
|
|
127376
|
+
description: "Serialized BranchDecision JSON for downstream actions (also exported as POSTMAN_BRANCH_DECISION)."
|
|
127377
|
+
},
|
|
127378
|
+
"spec-version-tag": {
|
|
127379
|
+
description: "Native Spec Hub version tag created after successful canonical repo-sync finalization."
|
|
127380
|
+
},
|
|
127381
|
+
"spec-version-url": {
|
|
127382
|
+
description: "Read-only URL for the tagged Spec Hub snapshot."
|
|
127238
127383
|
}
|
|
127239
127384
|
},
|
|
127240
127385
|
behavior: {
|
|
@@ -127302,53 +127447,6 @@ var PostmanAssetsClient = class {
|
|
|
127302
127447
|
}
|
|
127303
127448
|
};
|
|
127304
127449
|
|
|
127305
|
-
// src/lib/retry.ts
|
|
127306
|
-
function sleep(delayMs) {
|
|
127307
|
-
return new Promise((resolve3) => {
|
|
127308
|
-
setTimeout(resolve3, delayMs);
|
|
127309
|
-
});
|
|
127310
|
-
}
|
|
127311
|
-
function normalizeRetryOptions(options) {
|
|
127312
|
-
return {
|
|
127313
|
-
maxAttempts: Math.max(1, options.maxAttempts ?? 3),
|
|
127314
|
-
delayMs: Math.max(0, options.delayMs ?? 2e3),
|
|
127315
|
-
backoffMultiplier: Math.max(1, options.backoffMultiplier ?? 1),
|
|
127316
|
-
maxDelayMs: options.maxDelayMs === void 0 ? Number.POSITIVE_INFINITY : Math.max(0, options.maxDelayMs),
|
|
127317
|
-
onRetry: options.onRetry ?? (async () => void 0),
|
|
127318
|
-
shouldRetry: options.shouldRetry ?? (() => true),
|
|
127319
|
-
sleep: options.sleep ?? sleep
|
|
127320
|
-
};
|
|
127321
|
-
}
|
|
127322
|
-
async function retry(operation, options = {}) {
|
|
127323
|
-
const normalized = normalizeRetryOptions(options);
|
|
127324
|
-
let nextDelayMs = normalized.delayMs;
|
|
127325
|
-
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
127326
|
-
try {
|
|
127327
|
-
return await operation();
|
|
127328
|
-
} catch (error2) {
|
|
127329
|
-
const shouldRetry = attempt < normalized.maxAttempts && normalized.shouldRetry(error2, {
|
|
127330
|
-
attempt,
|
|
127331
|
-
maxAttempts: normalized.maxAttempts
|
|
127332
|
-
});
|
|
127333
|
-
if (!shouldRetry) {
|
|
127334
|
-
throw error2;
|
|
127335
|
-
}
|
|
127336
|
-
await normalized.onRetry({
|
|
127337
|
-
attempt,
|
|
127338
|
-
maxAttempts: normalized.maxAttempts,
|
|
127339
|
-
delayMs: nextDelayMs,
|
|
127340
|
-
error: error2
|
|
127341
|
-
});
|
|
127342
|
-
await normalized.sleep(nextDelayMs);
|
|
127343
|
-
nextDelayMs = Math.min(
|
|
127344
|
-
normalized.maxDelayMs,
|
|
127345
|
-
Math.round(nextDelayMs * normalized.backoffMultiplier)
|
|
127346
|
-
);
|
|
127347
|
-
}
|
|
127348
|
-
}
|
|
127349
|
-
throw new Error("Retry exhausted without returning or throwing");
|
|
127350
|
-
}
|
|
127351
|
-
|
|
127352
127450
|
// src/lib/postman/postman-gateway-assets-client.ts
|
|
127353
127451
|
var MAX_CREATE_FLIGHTS = 256;
|
|
127354
127452
|
var createFlights = /* @__PURE__ */ new Map();
|
|
@@ -127374,6 +127472,84 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127374
127472
|
if (!envelope) return null;
|
|
127375
127473
|
return this.asRecord(envelope.data) ?? envelope;
|
|
127376
127474
|
}
|
|
127475
|
+
/** Native Spec Hub tags attach to the latest changelog group. */
|
|
127476
|
+
async tagSpecVersion(specId, name) {
|
|
127477
|
+
const trimmed = name.trim().slice(0, 255);
|
|
127478
|
+
const response = await this.gateway.requestJson({
|
|
127479
|
+
service: "specification",
|
|
127480
|
+
method: "post",
|
|
127481
|
+
path: `/specifications/${specId}/tags`,
|
|
127482
|
+
body: { name: trimmed }
|
|
127483
|
+
});
|
|
127484
|
+
const record = this.dataOf(response) ?? {};
|
|
127485
|
+
return { id: String(record.id ?? "").trim(), name: String(record.name ?? trimmed).trim() };
|
|
127486
|
+
}
|
|
127487
|
+
async listSpecVersionTags(specId) {
|
|
127488
|
+
const response = await this.gateway.requestJson({
|
|
127489
|
+
service: "specification",
|
|
127490
|
+
method: "get",
|
|
127491
|
+
path: `/specifications/${specId}/tags`,
|
|
127492
|
+
query: { limit: "50" }
|
|
127493
|
+
});
|
|
127494
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127495
|
+
return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({
|
|
127496
|
+
id: String(entry.id ?? "").trim(),
|
|
127497
|
+
// listTags returns `message`; createTag returns `name`. Accept both.
|
|
127498
|
+
name: String(entry.name ?? entry.message ?? "").trim()
|
|
127499
|
+
})).filter((entry) => entry.id || entry.name);
|
|
127500
|
+
}
|
|
127501
|
+
async deleteCollection(collectionUid) {
|
|
127502
|
+
const bareId = String(collectionUid).split("-").slice(-5).join("-") || collectionUid;
|
|
127503
|
+
await this.gateway.requestJson({
|
|
127504
|
+
service: "collection",
|
|
127505
|
+
method: "delete",
|
|
127506
|
+
path: `/v3/collections/${bareId}`
|
|
127507
|
+
});
|
|
127508
|
+
}
|
|
127509
|
+
async listSpecifications(workspaceId) {
|
|
127510
|
+
const response = await this.gateway.requestJson({
|
|
127511
|
+
service: "specification",
|
|
127512
|
+
method: "get",
|
|
127513
|
+
path: `/specifications?containerType=workspace&containerId=${workspaceId}`
|
|
127514
|
+
});
|
|
127515
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127516
|
+
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);
|
|
127517
|
+
}
|
|
127518
|
+
async getSpecContent(specId) {
|
|
127519
|
+
const files = await this.gateway.requestJson({
|
|
127520
|
+
service: "specification",
|
|
127521
|
+
method: "get",
|
|
127522
|
+
path: `/specifications/${specId}/files`
|
|
127523
|
+
});
|
|
127524
|
+
const entries = Array.isArray(files?.data) ? files.data : [];
|
|
127525
|
+
const root = entries.map((entry) => this.asRecord(entry)).find((entry) => entry?.type === "ROOT") ?? entries.map((entry) => this.asRecord(entry)).find((entry) => entry !== null);
|
|
127526
|
+
const fileId = String(root?.id ?? "").trim();
|
|
127527
|
+
if (!fileId) return void 0;
|
|
127528
|
+
const file = await this.gateway.requestJson({
|
|
127529
|
+
service: "specification",
|
|
127530
|
+
method: "get",
|
|
127531
|
+
path: `/specifications/${specId}/files/${fileId}`,
|
|
127532
|
+
query: { fields: "content" }
|
|
127533
|
+
});
|
|
127534
|
+
const record = this.dataOf(file);
|
|
127535
|
+
return typeof record?.content === "string" ? record.content : void 0;
|
|
127536
|
+
}
|
|
127537
|
+
async listSpecCollections(specId) {
|
|
127538
|
+
const response = await this.gateway.requestJson({
|
|
127539
|
+
service: "specification",
|
|
127540
|
+
method: "get",
|
|
127541
|
+
path: `/specifications/${specId}/collections`
|
|
127542
|
+
});
|
|
127543
|
+
const data = Array.isArray(response?.data) ? response.data : [];
|
|
127544
|
+
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);
|
|
127545
|
+
}
|
|
127546
|
+
async deleteSpec(specId) {
|
|
127547
|
+
await this.gateway.requestJson({
|
|
127548
|
+
service: "specification",
|
|
127549
|
+
method: "delete",
|
|
127550
|
+
path: `/specifications/${specId}`
|
|
127551
|
+
});
|
|
127552
|
+
}
|
|
127377
127553
|
idOf(record) {
|
|
127378
127554
|
if (!record) return "";
|
|
127379
127555
|
const id = record.uid ?? record.id;
|
|
@@ -127433,6 +127609,14 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127433
127609
|
createFlights.set(key, { fingerprint, promise: pending });
|
|
127434
127610
|
return pending;
|
|
127435
127611
|
}
|
|
127612
|
+
/**
|
|
127613
|
+
* Reconcile an ambiguous create. Returns the adopted match, `undefined`
|
|
127614
|
+
* when every discovery read succeeded and found nothing (the create is
|
|
127615
|
+
* conclusively absent, so a fallback resend cannot duplicate), or `null`
|
|
127616
|
+
* when the outcome is inconclusive (non-ambiguous error, or a discovery
|
|
127617
|
+
* read itself failed — in that case a resend could duplicate a committed
|
|
127618
|
+
* twin and must not fire).
|
|
127619
|
+
*/
|
|
127436
127620
|
async discoverAfterAmbiguousCreate(discover, error2) {
|
|
127437
127621
|
if (!this.isAmbiguousCreateOutcome(error2)) {
|
|
127438
127622
|
return null;
|
|
@@ -127441,12 +127625,31 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127441
127625
|
if (attempt > 0) {
|
|
127442
127626
|
await this.sleep(this.reconcileDelayMs);
|
|
127443
127627
|
}
|
|
127444
|
-
|
|
127628
|
+
let found;
|
|
127629
|
+
try {
|
|
127630
|
+
found = await discover();
|
|
127631
|
+
} catch {
|
|
127632
|
+
return null;
|
|
127633
|
+
}
|
|
127445
127634
|
if (found) {
|
|
127446
127635
|
return found;
|
|
127447
127636
|
}
|
|
127448
127637
|
}
|
|
127449
|
-
return
|
|
127638
|
+
return void 0;
|
|
127639
|
+
}
|
|
127640
|
+
/**
|
|
127641
|
+
* After an ambiguous create failed AND discovery proved the asset absent,
|
|
127642
|
+
* re-attempt the create once with the cold `/_api` fallback enabled: the
|
|
127643
|
+
* reconcile above guarantees no committed twin, so the resend cannot
|
|
127644
|
+
* duplicate. Returns null when the resend also fails (caller rethrows the
|
|
127645
|
+
* original error).
|
|
127646
|
+
*/
|
|
127647
|
+
async resendAbsentCreate(operation) {
|
|
127648
|
+
try {
|
|
127649
|
+
return await operation();
|
|
127650
|
+
} catch {
|
|
127651
|
+
return null;
|
|
127652
|
+
}
|
|
127450
127653
|
}
|
|
127451
127654
|
publicEnvironmentUid(data, bareId) {
|
|
127452
127655
|
const owner = String(data?.owner ?? "").trim();
|
|
@@ -127494,17 +127697,18 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127494
127697
|
name: envName,
|
|
127495
127698
|
values: normalizedValues
|
|
127496
127699
|
};
|
|
127700
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
127701
|
+
{
|
|
127702
|
+
service: "sync",
|
|
127703
|
+
method: "post",
|
|
127704
|
+
path: `/environment/import?workspace=${ws}`,
|
|
127705
|
+
body,
|
|
127706
|
+
...fallback ? { fallback } : {}
|
|
127707
|
+
},
|
|
127708
|
+
{ retryTransient: false }
|
|
127709
|
+
);
|
|
127497
127710
|
try {
|
|
127498
|
-
const
|
|
127499
|
-
{
|
|
127500
|
-
service: "sync",
|
|
127501
|
-
method: "post",
|
|
127502
|
-
path: `/environment/import?workspace=${ws}`,
|
|
127503
|
-
body
|
|
127504
|
-
},
|
|
127505
|
-
{ retryTransient: false }
|
|
127506
|
-
);
|
|
127507
|
-
const data = this.dataOf(response);
|
|
127711
|
+
const data = this.dataOf(await send2());
|
|
127508
127712
|
const bareId = this.idOf(data);
|
|
127509
127713
|
if (!bareId) {
|
|
127510
127714
|
throw new Error("Environment import did not return a UID");
|
|
@@ -127519,6 +127723,15 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127519
127723
|
await this.updateEnvironment(adopted, envName, values);
|
|
127520
127724
|
return adopted;
|
|
127521
127725
|
}
|
|
127726
|
+
if (adopted === void 0) {
|
|
127727
|
+
const retried = await this.resendAbsentCreate(async () => {
|
|
127728
|
+
const data = this.dataOf(await send2("auto"));
|
|
127729
|
+
const bareId = this.idOf(data);
|
|
127730
|
+
if (!bareId) throw new Error("Environment import did not return a UID");
|
|
127731
|
+
return this.publicEnvironmentUid(data, bareId);
|
|
127732
|
+
});
|
|
127733
|
+
if (retried) return retried;
|
|
127734
|
+
}
|
|
127522
127735
|
throw error2;
|
|
127523
127736
|
}
|
|
127524
127737
|
});
|
|
@@ -127650,16 +127863,20 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127650
127863
|
private: false,
|
|
127651
127864
|
...environment ? { environment } : {}
|
|
127652
127865
|
};
|
|
127653
|
-
|
|
127654
|
-
|
|
127655
|
-
|
|
127656
|
-
|
|
127657
|
-
|
|
127658
|
-
|
|
127659
|
-
|
|
127660
|
-
|
|
127661
|
-
|
|
127662
|
-
|
|
127866
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
127867
|
+
{
|
|
127868
|
+
service: "mock",
|
|
127869
|
+
method: "post",
|
|
127870
|
+
path: `/mocks?workspace=${ws}`,
|
|
127871
|
+
body,
|
|
127872
|
+
...fallback ? { fallback } : {}
|
|
127873
|
+
},
|
|
127874
|
+
// Unsafe create: never blind re-POST. An ESOCKETTIMEDOUT after accept
|
|
127875
|
+
// may still have created the mock; reconcile via discovery below and
|
|
127876
|
+
// let the orchestrator retry the whole create-or-adopt cycle.
|
|
127877
|
+
{ retryTransient: false }
|
|
127878
|
+
);
|
|
127879
|
+
const parseMock = (response) => {
|
|
127663
127880
|
const record = this.dataOf(response);
|
|
127664
127881
|
const uid = this.idOf(record);
|
|
127665
127882
|
if (!uid) {
|
|
@@ -127669,6 +127886,9 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127669
127886
|
uid,
|
|
127670
127887
|
url: String(record?.url ?? record?.mockUrl ?? "").trim()
|
|
127671
127888
|
};
|
|
127889
|
+
};
|
|
127890
|
+
try {
|
|
127891
|
+
return parseMock(await send2());
|
|
127672
127892
|
} catch (error2) {
|
|
127673
127893
|
const adopted = await this.discoverAfterAmbiguousCreate(
|
|
127674
127894
|
() => this.findMockByCollection(collection, environment, mockName),
|
|
@@ -127677,6 +127897,10 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127677
127897
|
if (adopted) {
|
|
127678
127898
|
return { uid: adopted.uid, url: adopted.mockUrl };
|
|
127679
127899
|
}
|
|
127900
|
+
if (adopted === void 0) {
|
|
127901
|
+
const retried = await this.resendAbsentCreate(async () => parseMock(await send2("auto")));
|
|
127902
|
+
if (retried) return retried;
|
|
127903
|
+
}
|
|
127680
127904
|
throw error2;
|
|
127681
127905
|
}
|
|
127682
127906
|
});
|
|
@@ -127696,6 +127920,33 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127696
127920
|
environment: String(m.environment ?? "")
|
|
127697
127921
|
}));
|
|
127698
127922
|
}
|
|
127923
|
+
/**
|
|
127924
|
+
* Delete an environment through the sync service (GC path). The path id is
|
|
127925
|
+
* the bare model id (public uid tail), mirroring updateEnvironment.
|
|
127926
|
+
*/
|
|
127927
|
+
async deleteEnvironment(uid) {
|
|
127928
|
+
await this.gateway.requestJson({
|
|
127929
|
+
service: "sync",
|
|
127930
|
+
method: "delete",
|
|
127931
|
+
path: `/environment/${this.toModelId(uid)}`
|
|
127932
|
+
});
|
|
127933
|
+
}
|
|
127934
|
+
/** Delete a mock server (GC path). */
|
|
127935
|
+
async deleteMock(uid) {
|
|
127936
|
+
await this.gateway.requestJson({
|
|
127937
|
+
service: "mock",
|
|
127938
|
+
method: "delete",
|
|
127939
|
+
path: `/mocks/${this.toModelId(uid)}`
|
|
127940
|
+
});
|
|
127941
|
+
}
|
|
127942
|
+
/** Delete a collection-based monitor (jobTemplate) (GC path). */
|
|
127943
|
+
async deleteMonitor(uid) {
|
|
127944
|
+
await this.gateway.requestJson({
|
|
127945
|
+
service: "monitors",
|
|
127946
|
+
method: "delete",
|
|
127947
|
+
path: `/jobTemplates/${this.toModelId(uid)}`
|
|
127948
|
+
});
|
|
127949
|
+
}
|
|
127699
127950
|
async mockExists(uid) {
|
|
127700
127951
|
try {
|
|
127701
127952
|
await this.gateway.requestJson({
|
|
@@ -127756,21 +128007,25 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127756
128007
|
distribution: null,
|
|
127757
128008
|
...environment ? { environment } : {}
|
|
127758
128009
|
};
|
|
127759
|
-
|
|
127760
|
-
|
|
127761
|
-
|
|
127762
|
-
|
|
127763
|
-
|
|
127764
|
-
|
|
127765
|
-
|
|
127766
|
-
|
|
127767
|
-
|
|
127768
|
-
|
|
128010
|
+
const send2 = (fallback) => this.gateway.requestJson(
|
|
128011
|
+
{
|
|
128012
|
+
service: "monitors",
|
|
128013
|
+
method: "post",
|
|
128014
|
+
path: `/jobTemplates?workspace=${ws}`,
|
|
128015
|
+
body,
|
|
128016
|
+
...fallback ? { fallback } : {}
|
|
128017
|
+
},
|
|
128018
|
+
{ retryTransient: false }
|
|
128019
|
+
);
|
|
128020
|
+
const parseMonitor = (response) => {
|
|
127769
128021
|
const uid = this.idOf(this.dataOf(response));
|
|
127770
128022
|
if (!uid) {
|
|
127771
128023
|
throw new Error("Monitor create did not return a UID");
|
|
127772
128024
|
}
|
|
127773
128025
|
return uid;
|
|
128026
|
+
};
|
|
128027
|
+
try {
|
|
128028
|
+
return parseMonitor(await send2());
|
|
127774
128029
|
} catch (error2) {
|
|
127775
128030
|
const adopted = await this.discoverAfterAmbiguousCreate(
|
|
127776
128031
|
() => this.findMonitorByCollection(collection, environment, monitorName),
|
|
@@ -127779,6 +128034,10 @@ var PostmanGatewayAssetsClient = class {
|
|
|
127779
128034
|
if (adopted?.uid) {
|
|
127780
128035
|
return adopted.uid;
|
|
127781
128036
|
}
|
|
128037
|
+
if (adopted === void 0) {
|
|
128038
|
+
const retried = await this.resendAbsentCreate(async () => parseMonitor(await send2("auto")));
|
|
128039
|
+
if (retried) return retried;
|
|
128040
|
+
}
|
|
127782
128041
|
throw error2;
|
|
127783
128042
|
}
|
|
127784
128043
|
});
|
|
@@ -128007,6 +128266,7 @@ var AccessTokenGatewayClient = class {
|
|
|
128007
128266
|
fetchImpl;
|
|
128008
128267
|
secretMasker;
|
|
128009
128268
|
maxRetries;
|
|
128269
|
+
fallbackBaseUrl;
|
|
128010
128270
|
retryBaseDelayMs;
|
|
128011
128271
|
sleepImpl;
|
|
128012
128272
|
constructor(options) {
|
|
@@ -128019,6 +128279,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128019
128279
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
128020
128280
|
this.secretMasker = options.secretMasker ?? createSecretMasker([this.tokenProvider.current()]);
|
|
128021
128281
|
this.maxRetries = options.maxRetries ?? 3;
|
|
128282
|
+
const fallbackEnv = typeof process !== "undefined" ? process.env?.POSTMAN_ITEM_CREATE_FALLBACK : void 0;
|
|
128283
|
+
this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
|
|
128022
128284
|
this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
|
|
128023
128285
|
this.sleepImpl = options.sleepImpl ?? defaultSleep;
|
|
128024
128286
|
}
|
|
@@ -128037,8 +128299,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128037
128299
|
}
|
|
128038
128300
|
return headers;
|
|
128039
128301
|
}
|
|
128040
|
-
async send(request) {
|
|
128041
|
-
const url = `${this.bifrostBaseUrl}/ws/proxy`;
|
|
128302
|
+
async send(request, baseUrl) {
|
|
128303
|
+
const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
|
|
128042
128304
|
return this.fetchImpl(url, {
|
|
128043
128305
|
method: "POST",
|
|
128044
128306
|
headers: this.buildHeaders(request.headers),
|
|
@@ -128051,6 +128313,43 @@ var AccessTokenGatewayClient = class {
|
|
|
128051
128313
|
})
|
|
128052
128314
|
});
|
|
128053
128315
|
}
|
|
128316
|
+
/**
|
|
128317
|
+
* One cold, serial attempt against the fallback base URL after the primary
|
|
128318
|
+
* budget is exhausted on a transient failure. Never hedged in parallel with
|
|
128319
|
+
* the primary; only fires when the request would otherwise throw. Callers
|
|
128320
|
+
* with `retryTransient: false` still reconcile first — the fallback attempt
|
|
128321
|
+
* here is the resend, so it is only used for requests whose mutation is
|
|
128322
|
+
* known idempotent or already reconciled by the caller's adopt-on-ambiguous
|
|
128323
|
+
* loop.
|
|
128324
|
+
*/
|
|
128325
|
+
async tryFallback(request) {
|
|
128326
|
+
if (!this.fallbackBaseUrl) return null;
|
|
128327
|
+
try {
|
|
128328
|
+
return await this.send(request, this.fallbackBaseUrl);
|
|
128329
|
+
} catch {
|
|
128330
|
+
return null;
|
|
128331
|
+
}
|
|
128332
|
+
}
|
|
128333
|
+
/**
|
|
128334
|
+
* Run the fallback attempt and classify its response the same way the
|
|
128335
|
+
* primary path would. Returns the success response, or null when the
|
|
128336
|
+
* fallback also failed transiently (caller then throws the original error).
|
|
128337
|
+
* Non-transient fallback failures (4xx) surface as their own HttpError since
|
|
128338
|
+
* they are the freshest authoritative answer.
|
|
128339
|
+
*/
|
|
128340
|
+
fallbackEligible(request, retryTransient) {
|
|
128341
|
+
if (!this.fallbackBaseUrl) return false;
|
|
128342
|
+
return retryTransient || request.fallback === "auto";
|
|
128343
|
+
}
|
|
128344
|
+
async attemptFallback(request, retryTransient) {
|
|
128345
|
+
if (!this.fallbackEligible(request, retryTransient)) return null;
|
|
128346
|
+
const response = await this.tryFallback(request);
|
|
128347
|
+
if (!response) return null;
|
|
128348
|
+
if (response.ok) return response;
|
|
128349
|
+
const body = await response.text().catch(() => "");
|
|
128350
|
+
if (isRetryableSafeReadResponse(response.status)) return null;
|
|
128351
|
+
throw this.toHttpError(request, response, body);
|
|
128352
|
+
}
|
|
128054
128353
|
/**
|
|
128055
128354
|
* Send a gateway request, refreshing the token once on an auth failure and
|
|
128056
128355
|
* optionally retrying transient downstream failures (5xx / Bifrost read
|
|
@@ -128073,6 +128372,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128073
128372
|
await this.sleepImpl(delay);
|
|
128074
128373
|
continue;
|
|
128075
128374
|
}
|
|
128375
|
+
const fallbackResponse2 = await this.attemptFallback(request, retryTransient);
|
|
128376
|
+
if (fallbackResponse2) return fallbackResponse2;
|
|
128076
128377
|
throw error2;
|
|
128077
128378
|
}
|
|
128078
128379
|
if (response.ok) {
|
|
@@ -128094,6 +128395,8 @@ var AccessTokenGatewayClient = class {
|
|
|
128094
128395
|
await this.sleepImpl(delay);
|
|
128095
128396
|
continue;
|
|
128096
128397
|
}
|
|
128398
|
+
const fallbackResponse = await this.attemptFallback(request, retryTransient);
|
|
128399
|
+
if (fallbackResponse) return fallbackResponse;
|
|
128097
128400
|
throw this.toHttpError(request, response, body);
|
|
128098
128401
|
}
|
|
128099
128402
|
}
|
|
@@ -128202,6 +128505,331 @@ function validateCertMaterial(certBase64, keyBase64, passphrase) {
|
|
|
128202
128505
|
}
|
|
128203
128506
|
}
|
|
128204
128507
|
|
|
128508
|
+
// src/lib/repo/branch-decision.ts
|
|
128509
|
+
var import_node_fs3 = require("node:fs");
|
|
128510
|
+
var import_node_crypto3 = require("node:crypto");
|
|
128511
|
+
var ContractError = class extends Error {
|
|
128512
|
+
code;
|
|
128513
|
+
constructor(code, message) {
|
|
128514
|
+
super(`${code}: ${message}`);
|
|
128515
|
+
this.code = code;
|
|
128516
|
+
this.name = "ContractError";
|
|
128517
|
+
}
|
|
128518
|
+
};
|
|
128519
|
+
function clean(value) {
|
|
128520
|
+
const trimmed = (value ?? "").trim();
|
|
128521
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
128522
|
+
}
|
|
128523
|
+
function stripRefPrefix(ref) {
|
|
128524
|
+
const raw = clean(ref);
|
|
128525
|
+
if (!raw) {
|
|
128526
|
+
return { kind: "unknown" };
|
|
128527
|
+
}
|
|
128528
|
+
if (raw.startsWith("refs/heads/")) {
|
|
128529
|
+
return { name: raw.slice("refs/heads/".length), kind: "branch" };
|
|
128530
|
+
}
|
|
128531
|
+
if (raw.startsWith("refs/tags/")) {
|
|
128532
|
+
return { name: raw.slice("refs/tags/".length), kind: "tag" };
|
|
128533
|
+
}
|
|
128534
|
+
if (raw.startsWith("refs/pull/") || raw.startsWith("refs/merge")) {
|
|
128535
|
+
return { kind: "unknown" };
|
|
128536
|
+
}
|
|
128537
|
+
return { name: raw, kind: "branch" };
|
|
128538
|
+
}
|
|
128539
|
+
function detectProvider(env) {
|
|
128540
|
+
if (clean(env.GITHUB_ACTIONS) || clean(env.GITHUB_REPOSITORY)) return "github";
|
|
128541
|
+
if (clean(env.GITLAB_CI) || clean(env.CI_PROJECT_PATH)) return "gitlab";
|
|
128542
|
+
if (clean(env.BITBUCKET_REPO_SLUG) || clean(env.BITBUCKET_BRANCH)) return "bitbucket";
|
|
128543
|
+
if (clean(env.TF_BUILD) || clean(env.BUILD_REPOSITORY_URI)) return "azure-devops";
|
|
128544
|
+
return "unknown";
|
|
128545
|
+
}
|
|
128546
|
+
function readGithubEvent(env) {
|
|
128547
|
+
const path9 = clean(env.GITHUB_EVENT_PATH);
|
|
128548
|
+
if (!path9) return void 0;
|
|
128549
|
+
try {
|
|
128550
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(path9, "utf8"));
|
|
128551
|
+
} catch {
|
|
128552
|
+
return void 0;
|
|
128553
|
+
}
|
|
128554
|
+
}
|
|
128555
|
+
function resolveBranchIdentity(env = process.env, overrides = {}) {
|
|
128556
|
+
const provider = detectProvider(env);
|
|
128557
|
+
const explicitDefault = clean(overrides.defaultBranch);
|
|
128558
|
+
let headBranch;
|
|
128559
|
+
let rawRef;
|
|
128560
|
+
let refKind;
|
|
128561
|
+
let isPrContext = false;
|
|
128562
|
+
let isForkPr = false;
|
|
128563
|
+
let defaultBranch = explicitDefault;
|
|
128564
|
+
let headSha;
|
|
128565
|
+
switch (provider) {
|
|
128566
|
+
case "github": {
|
|
128567
|
+
const event = readGithubEvent(env);
|
|
128568
|
+
headSha = clean(env.GITHUB_SHA);
|
|
128569
|
+
defaultBranch ??= clean(event?.repository?.default_branch);
|
|
128570
|
+
const headRef = clean(env.GITHUB_HEAD_REF);
|
|
128571
|
+
if (headRef) {
|
|
128572
|
+
isPrContext = true;
|
|
128573
|
+
headBranch = headRef;
|
|
128574
|
+
rawRef = clean(env.GITHUB_REF) ?? headRef;
|
|
128575
|
+
refKind = "branch";
|
|
128576
|
+
const headRepo = event?.pull_request?.head?.repo?.full_name;
|
|
128577
|
+
const baseRepo = event?.pull_request?.base?.repo?.full_name ?? event?.repository?.full_name;
|
|
128578
|
+
isForkPr = Boolean(headRepo && baseRepo && headRepo !== baseRepo);
|
|
128579
|
+
headSha = clean(event?.pull_request?.head?.sha) ?? headSha;
|
|
128580
|
+
} else {
|
|
128581
|
+
rawRef = clean(env.GITHUB_REF) ?? clean(env.GITHUB_REF_NAME);
|
|
128582
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128583
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128584
|
+
refKind = parsed.kind;
|
|
128585
|
+
}
|
|
128586
|
+
break;
|
|
128587
|
+
}
|
|
128588
|
+
case "gitlab": {
|
|
128589
|
+
headSha = clean(env.CI_COMMIT_SHA);
|
|
128590
|
+
defaultBranch ??= clean(env.CI_DEFAULT_BRANCH);
|
|
128591
|
+
const mrSource = clean(env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME);
|
|
128592
|
+
if (mrSource) {
|
|
128593
|
+
isPrContext = true;
|
|
128594
|
+
headBranch = mrSource;
|
|
128595
|
+
rawRef = mrSource;
|
|
128596
|
+
refKind = "branch";
|
|
128597
|
+
const sourceProject = clean(env.CI_MERGE_REQUEST_SOURCE_PROJECT_ID);
|
|
128598
|
+
const targetProject = clean(env.CI_MERGE_REQUEST_PROJECT_ID);
|
|
128599
|
+
isForkPr = Boolean(sourceProject && targetProject && sourceProject !== targetProject);
|
|
128600
|
+
} else if (clean(env.CI_COMMIT_TAG)) {
|
|
128601
|
+
rawRef = clean(env.CI_COMMIT_TAG);
|
|
128602
|
+
refKind = "tag";
|
|
128603
|
+
} else {
|
|
128604
|
+
headBranch = clean(env.CI_COMMIT_BRANCH) ?? clean(env.CI_COMMIT_REF_NAME);
|
|
128605
|
+
rawRef = headBranch;
|
|
128606
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128607
|
+
}
|
|
128608
|
+
break;
|
|
128609
|
+
}
|
|
128610
|
+
case "bitbucket": {
|
|
128611
|
+
headSha = clean(env.BITBUCKET_COMMIT);
|
|
128612
|
+
if (clean(env.BITBUCKET_TAG)) {
|
|
128613
|
+
rawRef = clean(env.BITBUCKET_TAG);
|
|
128614
|
+
refKind = "tag";
|
|
128615
|
+
} else {
|
|
128616
|
+
headBranch = clean(env.BITBUCKET_BRANCH);
|
|
128617
|
+
rawRef = headBranch;
|
|
128618
|
+
refKind = headBranch ? "branch" : "unknown";
|
|
128619
|
+
isPrContext = Boolean(clean(env.BITBUCKET_PR_ID));
|
|
128620
|
+
}
|
|
128621
|
+
break;
|
|
128622
|
+
}
|
|
128623
|
+
case "azure-devops": {
|
|
128624
|
+
headSha = clean(env.BUILD_SOURCEVERSION);
|
|
128625
|
+
const prSource = clean(env.SYSTEM_PULLREQUEST_SOURCEBRANCH);
|
|
128626
|
+
if (prSource) {
|
|
128627
|
+
isPrContext = true;
|
|
128628
|
+
const parsed = stripRefPrefix(prSource);
|
|
128629
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128630
|
+
rawRef = prSource;
|
|
128631
|
+
refKind = parsed.kind;
|
|
128632
|
+
const forkFlag = clean(env.SYSTEM_PULLREQUEST_ISFORK);
|
|
128633
|
+
isForkPr = forkFlag?.toLowerCase() === "true";
|
|
128634
|
+
} else {
|
|
128635
|
+
rawRef = clean(env.BUILD_SOURCEBRANCH);
|
|
128636
|
+
const parsed = stripRefPrefix(rawRef);
|
|
128637
|
+
headBranch = parsed.kind === "branch" ? parsed.name : void 0;
|
|
128638
|
+
refKind = parsed.kind;
|
|
128639
|
+
}
|
|
128640
|
+
break;
|
|
128641
|
+
}
|
|
128642
|
+
default: {
|
|
128643
|
+
refKind = "unknown";
|
|
128644
|
+
}
|
|
128645
|
+
}
|
|
128646
|
+
if (refKind === "branch" && headBranch && defaultBranch && headBranch === defaultBranch) {
|
|
128647
|
+
refKind = "default-branch";
|
|
128648
|
+
}
|
|
128649
|
+
return { provider, headBranch, rawRef, defaultBranch, refKind, isPrContext, isForkPr, headSha };
|
|
128650
|
+
}
|
|
128651
|
+
function parseChannelRules(input) {
|
|
128652
|
+
const raw = clean(input);
|
|
128653
|
+
const rules = [];
|
|
128654
|
+
for (const part of raw ? raw.split(",") : []) {
|
|
128655
|
+
const entry = part.trim();
|
|
128656
|
+
if (!entry) continue;
|
|
128657
|
+
const eq = entry.indexOf("=");
|
|
128658
|
+
if (eq <= 0 || eq === entry.length - 1) {
|
|
128659
|
+
throw new ContractError(
|
|
128660
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128661
|
+
`channels entry "${entry}" must be <branch-or-glob>=<CODE>`
|
|
128662
|
+
);
|
|
128663
|
+
}
|
|
128664
|
+
const pattern = entry.slice(0, eq).trim();
|
|
128665
|
+
const code = entry.slice(eq + 1).trim().toUpperCase();
|
|
128666
|
+
if (!/^[A-Z][A-Z0-9_-]{0,15}$/.test(code)) {
|
|
128667
|
+
throw new ContractError(
|
|
128668
|
+
"CONTRACT_CHANNELS_INPUT_INVALID",
|
|
128669
|
+
`channel code "${code}" must be 1-16 chars, A-Z 0-9 _ -, starting with a letter`
|
|
128670
|
+
);
|
|
128671
|
+
}
|
|
128672
|
+
rules.push({ pattern, code });
|
|
128673
|
+
}
|
|
128674
|
+
if (!rules.some((rule) => rule.pattern === "release/*")) {
|
|
128675
|
+
rules.push({ pattern: "release/*", code: "RC" });
|
|
128676
|
+
}
|
|
128677
|
+
return rules;
|
|
128678
|
+
}
|
|
128679
|
+
function matchChannel(branch, rules) {
|
|
128680
|
+
for (const rule of rules) {
|
|
128681
|
+
if (rule.pattern.endsWith("*")) {
|
|
128682
|
+
const prefix = rule.pattern.slice(0, -1);
|
|
128683
|
+
if (branch.startsWith(prefix)) return rule;
|
|
128684
|
+
} else if (branch === rule.pattern) {
|
|
128685
|
+
return rule;
|
|
128686
|
+
}
|
|
128687
|
+
}
|
|
128688
|
+
return void 0;
|
|
128689
|
+
}
|
|
128690
|
+
function resolveBranchDecision(options) {
|
|
128691
|
+
const { strategy, identity } = options;
|
|
128692
|
+
const channels = options.channels ?? [];
|
|
128693
|
+
if (strategy === "legacy") {
|
|
128694
|
+
return {
|
|
128695
|
+
tier: "legacy",
|
|
128696
|
+
strategy,
|
|
128697
|
+
identity,
|
|
128698
|
+
canonicalBranch: clean(options.canonicalBranch) ?? identity.defaultBranch,
|
|
128699
|
+
reason: "branch-strategy legacy: branch-blind pre-v2 behavior"
|
|
128700
|
+
};
|
|
128701
|
+
}
|
|
128702
|
+
const canonicalBranch = clean(options.canonicalBranch) ?? identity.defaultBranch;
|
|
128703
|
+
if (!canonicalBranch) {
|
|
128704
|
+
throw new ContractError(
|
|
128705
|
+
"CONTRACT_DEFAULT_BRANCH_UNRESOLVED",
|
|
128706
|
+
`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.`
|
|
128707
|
+
);
|
|
128708
|
+
}
|
|
128709
|
+
if (identity.refKind === "tag" || identity.refKind === "unknown" || !identity.headBranch) {
|
|
128710
|
+
return {
|
|
128711
|
+
tier: "gated",
|
|
128712
|
+
strategy,
|
|
128713
|
+
identity,
|
|
128714
|
+
canonicalBranch,
|
|
128715
|
+
reason: `ref kind ${identity.refKind}: never canonical/preview-eligible; no-op with annotation`
|
|
128716
|
+
};
|
|
128717
|
+
}
|
|
128718
|
+
if (identity.headBranch === canonicalBranch) {
|
|
128719
|
+
return {
|
|
128720
|
+
tier: "canonical",
|
|
128721
|
+
strategy,
|
|
128722
|
+
identity,
|
|
128723
|
+
canonicalBranch,
|
|
128724
|
+
reason: `head branch equals canonical branch ${canonicalBranch}`
|
|
128725
|
+
};
|
|
128726
|
+
}
|
|
128727
|
+
const channel = matchChannel(identity.headBranch, channels);
|
|
128728
|
+
if (channel) {
|
|
128729
|
+
return {
|
|
128730
|
+
tier: "channel",
|
|
128731
|
+
strategy,
|
|
128732
|
+
identity,
|
|
128733
|
+
canonicalBranch,
|
|
128734
|
+
channel,
|
|
128735
|
+
reason: `branch ${identity.headBranch} matches channel ${channel.pattern}=${channel.code}`
|
|
128736
|
+
};
|
|
128737
|
+
}
|
|
128738
|
+
if (strategy === "preview") {
|
|
128739
|
+
if (identity.isForkPr) {
|
|
128740
|
+
return {
|
|
128741
|
+
tier: "gated",
|
|
128742
|
+
strategy,
|
|
128743
|
+
identity,
|
|
128744
|
+
canonicalBranch,
|
|
128745
|
+
reason: "fork PR: preview-ineligible (same-repo gate), gated instead"
|
|
128746
|
+
};
|
|
128747
|
+
}
|
|
128748
|
+
return {
|
|
128749
|
+
tier: "preview",
|
|
128750
|
+
strategy,
|
|
128751
|
+
identity,
|
|
128752
|
+
canonicalBranch,
|
|
128753
|
+
reason: `branch ${identity.headBranch} under branch-strategy preview`
|
|
128754
|
+
};
|
|
128755
|
+
}
|
|
128756
|
+
return {
|
|
128757
|
+
tier: "gated",
|
|
128758
|
+
strategy,
|
|
128759
|
+
identity,
|
|
128760
|
+
canonicalBranch,
|
|
128761
|
+
reason: `branch ${identity.headBranch} under branch-strategy publish-gate`
|
|
128762
|
+
};
|
|
128763
|
+
}
|
|
128764
|
+
var BRANCH_DECISION_ENV = "POSTMAN_BRANCH_DECISION";
|
|
128765
|
+
function serializeBranchDecision(decision) {
|
|
128766
|
+
return JSON.stringify(decision);
|
|
128767
|
+
}
|
|
128768
|
+
function parseBranchDecision(raw) {
|
|
128769
|
+
const value = clean(raw);
|
|
128770
|
+
if (!value) return void 0;
|
|
128771
|
+
let parsed;
|
|
128772
|
+
try {
|
|
128773
|
+
parsed = JSON.parse(value);
|
|
128774
|
+
} catch {
|
|
128775
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION is not valid JSON");
|
|
128776
|
+
}
|
|
128777
|
+
const candidate = parsed;
|
|
128778
|
+
const tiers = ["canonical", "channel", "preview", "gated", "legacy"];
|
|
128779
|
+
if (!candidate || typeof candidate !== "object" || !tiers.includes(candidate.tier) || !candidate.identity) {
|
|
128780
|
+
throw new ContractError("CONTRACT_BRANCH_DECISION_INVALID", "POSTMAN_BRANCH_DECISION does not carry a valid BranchDecision");
|
|
128781
|
+
}
|
|
128782
|
+
return candidate;
|
|
128783
|
+
}
|
|
128784
|
+
function resolveEffectiveBranchDecision(options, env = process.env) {
|
|
128785
|
+
const inherited = parseBranchDecision(env[BRANCH_DECISION_ENV]);
|
|
128786
|
+
if (inherited) return inherited;
|
|
128787
|
+
return resolveBranchDecision(options);
|
|
128788
|
+
}
|
|
128789
|
+
var PREVIEW_SLUG_MAX = 30;
|
|
128790
|
+
function buildBranchSlug(rawBranch) {
|
|
128791
|
+
const sanitized = rawBranch.replace(/^refs\/heads\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128792
|
+
const truncated = sanitized.slice(0, PREVIEW_SLUG_MAX);
|
|
128793
|
+
const lossy = truncated !== rawBranch.replace(/^refs\/heads\//, "");
|
|
128794
|
+
if (!lossy) {
|
|
128795
|
+
return { suffix: truncated, slug: truncated, lossy };
|
|
128796
|
+
}
|
|
128797
|
+
const hash = (0, import_node_crypto3.createHash)("sha256").update(rawBranch).digest("hex").slice(0, 6);
|
|
128798
|
+
return { suffix: `${truncated}-${hash}`, slug: truncated, lossy };
|
|
128799
|
+
}
|
|
128800
|
+
function previewAssetName(baseName, rawBranch) {
|
|
128801
|
+
return `${baseName} @${buildBranchSlug(rawBranch).suffix}`;
|
|
128802
|
+
}
|
|
128803
|
+
function channelAssetName(baseName, code) {
|
|
128804
|
+
return `[${code}] ${baseName}`;
|
|
128805
|
+
}
|
|
128806
|
+
var MARKER_KEY = "x-pm-onboarding";
|
|
128807
|
+
function parseAssetMarker(description) {
|
|
128808
|
+
if (!description) return void 0;
|
|
128809
|
+
const index = description.indexOf(`${MARKER_KEY}:`);
|
|
128810
|
+
if (index === -1) return void 0;
|
|
128811
|
+
const jsonStart = description.indexOf("{", index);
|
|
128812
|
+
if (jsonStart === -1) return void 0;
|
|
128813
|
+
let depth = 0;
|
|
128814
|
+
for (let i = jsonStart; i < description.length; i += 1) {
|
|
128815
|
+
const ch = description[i];
|
|
128816
|
+
if (ch === "{") depth += 1;
|
|
128817
|
+
else if (ch === "}") {
|
|
128818
|
+
depth -= 1;
|
|
128819
|
+
if (depth === 0) {
|
|
128820
|
+
try {
|
|
128821
|
+
const parsed = JSON.parse(description.slice(jsonStart, i + 1));
|
|
128822
|
+
if (parsed && typeof parsed === "object" && parsed.repo && parsed.role) return parsed;
|
|
128823
|
+
} catch {
|
|
128824
|
+
return void 0;
|
|
128825
|
+
}
|
|
128826
|
+
return void 0;
|
|
128827
|
+
}
|
|
128828
|
+
}
|
|
128829
|
+
}
|
|
128830
|
+
return void 0;
|
|
128831
|
+
}
|
|
128832
|
+
|
|
128205
128833
|
// src/index.ts
|
|
128206
128834
|
function parseBooleanInput(value, defaultValue) {
|
|
128207
128835
|
const normalized = String(value || "").trim().toLowerCase();
|
|
@@ -128220,16 +128848,16 @@ function getInput2(name, env = process.env) {
|
|
|
128220
128848
|
const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
|
|
128221
128849
|
const hasNormalized = normalizedRaw !== void 0;
|
|
128222
128850
|
const hasRunner = runnerRaw !== void 0;
|
|
128851
|
+
const normalizedValue = normalizeInputValue(normalizedRaw);
|
|
128852
|
+
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128223
128853
|
if (hasNormalized && hasRunner) {
|
|
128224
|
-
|
|
128225
|
-
const runnerValue = normalizeInputValue(runnerRaw);
|
|
128226
|
-
if (normalizedValue !== runnerValue) {
|
|
128854
|
+
if (normalizedValue && runnerValue && normalizedValue !== runnerValue) {
|
|
128227
128855
|
throw new Error(
|
|
128228
128856
|
`Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
|
|
128229
128857
|
);
|
|
128230
128858
|
}
|
|
128231
128859
|
}
|
|
128232
|
-
return
|
|
128860
|
+
return normalizedValue || runnerValue;
|
|
128233
128861
|
}
|
|
128234
128862
|
function hasInput(name, env = process.env) {
|
|
128235
128863
|
const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
|
|
@@ -128291,6 +128919,17 @@ function parseCredentialPreflight(value) {
|
|
|
128291
128919
|
`Unsupported credential-preflight "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128292
128920
|
);
|
|
128293
128921
|
}
|
|
128922
|
+
function parseBranchStrategy(value) {
|
|
128923
|
+
const definition = postmanRepoSyncActionContract.inputs["branch-strategy"];
|
|
128924
|
+
const allowed = definition.allowedValues ?? [];
|
|
128925
|
+
const normalized = String(value || "").trim() || (definition.default ?? "legacy");
|
|
128926
|
+
if (allowed.includes(normalized)) {
|
|
128927
|
+
return normalized;
|
|
128928
|
+
}
|
|
128929
|
+
throw new Error(
|
|
128930
|
+
`Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
|
|
128931
|
+
);
|
|
128932
|
+
}
|
|
128294
128933
|
function normalizeReleaseLabel(value) {
|
|
128295
128934
|
const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
|
|
128296
128935
|
return cleaned;
|
|
@@ -128335,6 +128974,7 @@ function resolveInputs(env = process.env) {
|
|
|
128335
128974
|
smokeCollectionId: getInput2("smoke-collection-id", env),
|
|
128336
128975
|
contractCollectionId: getInput2("contract-collection-id", env),
|
|
128337
128976
|
specId: getInput2("spec-id", env),
|
|
128977
|
+
specContentChanged: parseBooleanInput(getInput2("spec-content-changed", env), true),
|
|
128338
128978
|
specPath: getInput2("spec-path", env),
|
|
128339
128979
|
collectionSyncMode: normalizeCollectionSyncMode(getInput2("collection-sync-mode", env) || "refresh"),
|
|
128340
128980
|
specSyncMode: normalizeSpecSyncMode(getInput2("spec-sync-mode", env) || "update"),
|
|
@@ -128357,6 +128997,10 @@ function resolveInputs(env = process.env) {
|
|
|
128357
128997
|
postmanApiKey: getInput2("postman-api-key", env),
|
|
128358
128998
|
postmanAccessToken: getInput2("postman-access-token", env),
|
|
128359
128999
|
credentialPreflight: parseCredentialPreflight(getInput2("credential-preflight", env)),
|
|
129000
|
+
branchStrategy: parseBranchStrategy(getInput2("branch-strategy", env)),
|
|
129001
|
+
canonicalBranch: getInput2("canonical-branch", env) || void 0,
|
|
129002
|
+
channels: getInput2("channels", env) || void 0,
|
|
129003
|
+
previewTtlDays: Math.max(1, Number.parseInt(getInput2("preview-ttl", env) || "30", 10) || 30),
|
|
128360
129004
|
adoToken: getInput2("ado-token", env) || normalizeInputValue(env.SYSTEM_ACCESSTOKEN),
|
|
128361
129005
|
githubToken: getInput2("github-token", env),
|
|
128362
129006
|
ghFallbackToken: getInput2("gh-fallback-token", env),
|
|
@@ -128379,10 +129023,45 @@ function resolveInputs(env = process.env) {
|
|
|
128379
129023
|
postmanStack,
|
|
128380
129024
|
postmanApiBase: endpointProfile.apiBaseUrl,
|
|
128381
129025
|
postmanBifrostBase: endpointProfile.bifrostBaseUrl,
|
|
129026
|
+
postmanFallbackBase: endpointProfile.fallbackBaseUrl,
|
|
128382
129027
|
postmanCliInstallUrl: endpointProfile.cliInstallUrl,
|
|
128383
129028
|
postmanIapubBase: endpointProfile.iapubBaseUrl
|
|
128384
129029
|
};
|
|
128385
129030
|
}
|
|
129031
|
+
function buildBranchAssetMarker(decision, inputs, now = /* @__PURE__ */ new Date()) {
|
|
129032
|
+
if (decision.tier !== "preview" && decision.tier !== "channel") {
|
|
129033
|
+
return void 0;
|
|
129034
|
+
}
|
|
129035
|
+
const rawBranch = decision.identity.headBranch;
|
|
129036
|
+
const repo = inputs.repoUrl || inputs.repository;
|
|
129037
|
+
if (!rawBranch || !repo) {
|
|
129038
|
+
return void 0;
|
|
129039
|
+
}
|
|
129040
|
+
const ttlMs = inputs.previewTtlDays * 24 * 60 * 60 * 1e3;
|
|
129041
|
+
return {
|
|
129042
|
+
repo,
|
|
129043
|
+
rawBranch,
|
|
129044
|
+
sanitizedBranch: buildBranchSlug(rawBranch).suffix,
|
|
129045
|
+
role: decision.tier,
|
|
129046
|
+
headSha: decision.identity.headSha,
|
|
129047
|
+
createdAt: now.toISOString(),
|
|
129048
|
+
lastSyncedAt: now.toISOString(),
|
|
129049
|
+
...decision.tier === "preview" ? { expiresAt: new Date(now.getTime() + ttlMs).toISOString() } : {}
|
|
129050
|
+
};
|
|
129051
|
+
}
|
|
129052
|
+
function assertBranchAssetIds(inputs, decision, branchOwnedIds = process.env.POSTMAN_BRANCH_ASSET_IDS === "owned") {
|
|
129053
|
+
if (decision.tier === "legacy" || decision.tier === "canonical" || branchOwnedIds) return;
|
|
129054
|
+
const provided = [
|
|
129055
|
+
["baseline-collection-id", inputs.baselineCollectionId],
|
|
129056
|
+
["smoke-collection-id", inputs.smokeCollectionId],
|
|
129057
|
+
["contract-collection-id", inputs.contractCollectionId]
|
|
129058
|
+
].filter(([, value]) => Boolean(value));
|
|
129059
|
+
if (provided.length > 0) {
|
|
129060
|
+
throw new Error(
|
|
129061
|
+
`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.`
|
|
129062
|
+
);
|
|
129063
|
+
}
|
|
129064
|
+
}
|
|
128386
129065
|
function buildEnvironmentValues(envName, baseUrl) {
|
|
128387
129066
|
return [
|
|
128388
129067
|
{ key: "baseUrl", value: baseUrl, type: "default" },
|
|
@@ -128395,12 +129074,48 @@ function buildEnvironmentValues(envName, baseUrl) {
|
|
|
128395
129074
|
];
|
|
128396
129075
|
}
|
|
128397
129076
|
var LEGACY_BASELINE_COLLECTION_PREFIX = "[Baseline]";
|
|
129077
|
+
var RESOURCES_STATE_VERSION = 2;
|
|
129078
|
+
var SUPPORTED_STATE_VERSIONS = /* @__PURE__ */ new Set([1, RESOURCES_STATE_VERSION]);
|
|
129079
|
+
var StateUnreadableError = class extends Error {
|
|
129080
|
+
code = "CONTRACT_STATE_UNREADABLE";
|
|
129081
|
+
constructor(message) {
|
|
129082
|
+
super(`CONTRACT_STATE_UNREADABLE: ${message}`);
|
|
129083
|
+
this.name = "StateUnreadableError";
|
|
129084
|
+
}
|
|
129085
|
+
};
|
|
128398
129086
|
function readResourcesState() {
|
|
129087
|
+
let raw;
|
|
128399
129088
|
try {
|
|
128400
|
-
|
|
129089
|
+
raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
|
|
128401
129090
|
} catch {
|
|
128402
129091
|
return null;
|
|
128403
129092
|
}
|
|
129093
|
+
let parsed;
|
|
129094
|
+
try {
|
|
129095
|
+
parsed = load(raw);
|
|
129096
|
+
} catch (error2) {
|
|
129097
|
+
throw new StateUnreadableError(
|
|
129098
|
+
`.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.`
|
|
129099
|
+
);
|
|
129100
|
+
}
|
|
129101
|
+
if (parsed === null || parsed === void 0) {
|
|
129102
|
+
return null;
|
|
129103
|
+
}
|
|
129104
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
129105
|
+
throw new StateUnreadableError(
|
|
129106
|
+
".postman/resources.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to treat tracked state as absent."
|
|
129107
|
+
);
|
|
129108
|
+
}
|
|
129109
|
+
const state = parsed;
|
|
129110
|
+
if (state.version !== void 0 && !SUPPORTED_STATE_VERSIONS.has(Number(state.version))) {
|
|
129111
|
+
throw new StateUnreadableError(
|
|
129112
|
+
`.postman/resources.yaml declares unsupported state version ${String(state.version)} (supported: 1, ${RESOURCES_STATE_VERSION}). Upgrade the action or fix the file.`
|
|
129113
|
+
);
|
|
129114
|
+
}
|
|
129115
|
+
if (state.canonical && !state.cloudResources) {
|
|
129116
|
+
state.cloudResources = { ...state.canonical };
|
|
129117
|
+
}
|
|
129118
|
+
return state;
|
|
128404
129119
|
}
|
|
128405
129120
|
function findCloudResourceId(map, matcher) {
|
|
128406
129121
|
if (!map) {
|
|
@@ -128435,7 +129150,7 @@ function isOpenApiSpecFile(filePath) {
|
|
|
128435
129150
|
return false;
|
|
128436
129151
|
}
|
|
128437
129152
|
try {
|
|
128438
|
-
const raw = (0,
|
|
129153
|
+
const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
|
|
128439
129154
|
const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
|
|
128440
129155
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
128441
129156
|
return false;
|
|
@@ -128464,7 +129179,7 @@ function scanLocalSpecReferences(baseDir = ".") {
|
|
|
128464
129179
|
const found = /* @__PURE__ */ new Set();
|
|
128465
129180
|
const refs = [];
|
|
128466
129181
|
const visit2 = (currentDir) => {
|
|
128467
|
-
for (const entry of (0,
|
|
129182
|
+
for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
|
|
128468
129183
|
if (ignoredDirs.has(entry.name)) {
|
|
128469
129184
|
continue;
|
|
128470
129185
|
}
|
|
@@ -128494,7 +129209,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
|
|
|
128494
129209
|
const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
|
|
128495
129210
|
if (normalizedExplicitPath) {
|
|
128496
129211
|
const explicitFullPath = path8.resolve(normalizedExplicitPath);
|
|
128497
|
-
if ((0,
|
|
129212
|
+
if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
|
|
128498
129213
|
return {
|
|
128499
129214
|
repoRelativePath: normalizedExplicitPath,
|
|
128500
129215
|
configRelativePath: normalizeToPosix(path8.join("..", normalizedExplicitPath))
|
|
@@ -128521,7 +129236,11 @@ function createOutputs(inputs) {
|
|
|
128521
129236
|
"mock-url": "",
|
|
128522
129237
|
"monitor-id": "",
|
|
128523
129238
|
"repo-sync-summary-json": "{}",
|
|
128524
|
-
"commit-sha": ""
|
|
129239
|
+
"commit-sha": "",
|
|
129240
|
+
"sync-status": "",
|
|
129241
|
+
"branch-decision": "",
|
|
129242
|
+
"spec-version-tag": "",
|
|
129243
|
+
"spec-version-url": ""
|
|
128525
129244
|
};
|
|
128526
129245
|
}
|
|
128527
129246
|
function readActionInputs(actionCore) {
|
|
@@ -128566,6 +129285,10 @@ function readActionInputs(actionCore) {
|
|
|
128566
129285
|
INPUT_POSTMAN_API_KEY: postmanApiKey,
|
|
128567
129286
|
INPUT_POSTMAN_ACCESS_TOKEN: postmanAccessToken,
|
|
128568
129287
|
INPUT_CREDENTIAL_PREFLIGHT: readInput(actionCore, "credential-preflight") || "warn",
|
|
129288
|
+
INPUT_BRANCH_STRATEGY: readInput(actionCore, "branch-strategy"),
|
|
129289
|
+
INPUT_CANONICAL_BRANCH: readInput(actionCore, "canonical-branch"),
|
|
129290
|
+
INPUT_CHANNELS: readInput(actionCore, "channels"),
|
|
129291
|
+
INPUT_PREVIEW_TTL: readInput(actionCore, "preview-ttl"),
|
|
128569
129292
|
INPUT_ADO_TOKEN: adoToken,
|
|
128570
129293
|
INPUT_GITHUB_TOKEN: githubToken,
|
|
128571
129294
|
INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
|
|
@@ -128680,7 +129403,7 @@ async function persistSslSecrets(inputs, actionCore, actionExec, repository, env
|
|
|
128680
129403
|
);
|
|
128681
129404
|
}
|
|
128682
129405
|
}
|
|
128683
|
-
async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
129406
|
+
async function upsertEnvironments(inputs, dependencies, resourcesState, assetMarker) {
|
|
128684
129407
|
const envUids = {
|
|
128685
129408
|
...getEnvironmentUidsFromResources(resourcesState),
|
|
128686
129409
|
...inputs.environmentUids
|
|
@@ -128690,7 +129413,6 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128690
129413
|
}
|
|
128691
129414
|
for (const envName of inputs.environments) {
|
|
128692
129415
|
const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
|
|
128693
|
-
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
128694
129416
|
const displayName = `${inputs.projectName} - ${envName}`;
|
|
128695
129417
|
let existingUid = String(envUids[envName] || "").trim();
|
|
128696
129418
|
if (!existingUid) {
|
|
@@ -128706,10 +129428,27 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128706
129428
|
}
|
|
128707
129429
|
}
|
|
128708
129430
|
if (existingUid) {
|
|
128709
|
-
|
|
129431
|
+
let marker = assetMarker;
|
|
129432
|
+
if (marker) {
|
|
129433
|
+
try {
|
|
129434
|
+
const existing = await dependencies.postman.getEnvironment(existingUid);
|
|
129435
|
+
const values3 = existing.data?.values ?? existing.values ?? [];
|
|
129436
|
+
const prior = values3.find((value) => value.key === "x-pm-onboarding")?.value;
|
|
129437
|
+
const priorMarker = parseAssetMarker(prior ? `x-pm-onboarding: ${prior}` : void 0);
|
|
129438
|
+
if (priorMarker?.repo === marker.repo && priorMarker.rawBranch === marker.rawBranch) {
|
|
129439
|
+
marker = { ...marker, createdAt: priorMarker.createdAt };
|
|
129440
|
+
}
|
|
129441
|
+
} catch {
|
|
129442
|
+
}
|
|
129443
|
+
}
|
|
129444
|
+
const values2 = buildEnvironmentValues(envName, runtimeUrl);
|
|
129445
|
+
if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
|
|
129446
|
+
await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
|
|
128710
129447
|
envUids[envName] = existingUid;
|
|
128711
129448
|
continue;
|
|
128712
129449
|
}
|
|
129450
|
+
const values = buildEnvironmentValues(envName, runtimeUrl);
|
|
129451
|
+
if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
|
|
128713
129452
|
envUids[envName] = await dependencies.postman.createEnvironment(
|
|
128714
129453
|
inputs.workspaceId,
|
|
128715
129454
|
displayName,
|
|
@@ -128719,7 +129458,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
|
|
|
128719
129458
|
return envUids;
|
|
128720
129459
|
}
|
|
128721
129460
|
function ensureDir(path9) {
|
|
128722
|
-
(0,
|
|
129461
|
+
(0, import_node_fs4.mkdirSync)(path9, { recursive: true });
|
|
128723
129462
|
}
|
|
128724
129463
|
function getCollectionDirectoryName(kind, projectName) {
|
|
128725
129464
|
if (kind === "Baseline") {
|
|
@@ -128756,7 +129495,7 @@ function stripVolatileFields(obj) {
|
|
|
128756
129495
|
}
|
|
128757
129496
|
function writeJsonFile(path9, content, normalize3 = false) {
|
|
128758
129497
|
const data = normalize3 ? stripVolatileFields(content) : content;
|
|
128759
|
-
(0,
|
|
129498
|
+
(0, import_node_fs4.writeFileSync)(path9, JSON.stringify(data, null, 2));
|
|
128760
129499
|
}
|
|
128761
129500
|
function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
|
|
128762
129501
|
if (specSyncMode !== "version") {
|
|
@@ -128780,31 +129519,30 @@ function resolveDurableWorkspaceId(options) {
|
|
|
128780
129519
|
}
|
|
128781
129520
|
return prior === candidate ? prior : void 0;
|
|
128782
129521
|
}
|
|
128783
|
-
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs) {
|
|
128784
|
-
const manifest = {};
|
|
129522
|
+
function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId, existingSpecs, priorState) {
|
|
129523
|
+
const manifest = { ...priorState ?? {} };
|
|
129524
|
+
delete manifest.version;
|
|
129525
|
+
delete manifest.workspace;
|
|
129526
|
+
delete manifest.localResources;
|
|
129527
|
+
delete manifest.cloudResources;
|
|
129528
|
+
delete manifest.canonical;
|
|
129529
|
+
manifest.version = RESOURCES_STATE_VERSION;
|
|
128785
129530
|
if (workspaceId) {
|
|
128786
129531
|
manifest.workspace = { id: workspaceId };
|
|
128787
129532
|
}
|
|
128788
|
-
const localResources = {};
|
|
128789
129533
|
const cloudResources = {};
|
|
128790
129534
|
const collectionKeys = Object.keys(collectionMap);
|
|
128791
129535
|
if (collectionKeys.length > 0) {
|
|
128792
|
-
localResources.collections = collectionKeys;
|
|
128793
129536
|
cloudResources.collections = collectionMap;
|
|
128794
129537
|
}
|
|
128795
129538
|
const envEntries = Object.entries(envMap);
|
|
128796
129539
|
if (envEntries.length > 0) {
|
|
128797
|
-
localResources.environments = envEntries.map(
|
|
128798
|
-
([envName]) => `../${artifactDir}/environments/${envName}.postman_environment.json`
|
|
128799
|
-
);
|
|
128800
129540
|
cloudResources.environments = {};
|
|
128801
129541
|
for (const [envName, envUid] of envEntries) {
|
|
128802
129542
|
cloudResources.environments[`../${artifactDir}/environments/${envName}.postman_environment.json`] = envUid;
|
|
128803
129543
|
}
|
|
128804
129544
|
}
|
|
128805
|
-
|
|
128806
|
-
localResources.specs = localSpecRefs;
|
|
128807
|
-
}
|
|
129545
|
+
void localSpecRefs;
|
|
128808
129546
|
const specs = { ...existingSpecs || {} };
|
|
128809
129547
|
if (mappedSpecRef && specId) {
|
|
128810
129548
|
specs[mappedSpecRef] = specId;
|
|
@@ -128812,11 +129550,8 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
|
|
|
128812
129550
|
if (Object.keys(specs).length > 0) {
|
|
128813
129551
|
cloudResources.specs = specs;
|
|
128814
129552
|
}
|
|
128815
|
-
if (Object.keys(localResources).length > 0) {
|
|
128816
|
-
manifest.localResources = localResources;
|
|
128817
|
-
}
|
|
128818
129553
|
if (Object.keys(cloudResources).length > 0) {
|
|
128819
|
-
manifest.
|
|
129554
|
+
manifest.canonical = cloudResources;
|
|
128820
129555
|
}
|
|
128821
129556
|
return dump(manifest, {
|
|
128822
129557
|
lineWidth: -1,
|
|
@@ -128857,21 +129592,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
|
|
|
128857
129592
|
if (!rawPath || hasControlCharacter2(originalPath) || path8.isAbsolute(rawPath) || path8.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
|
|
128858
129593
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128859
129594
|
}
|
|
128860
|
-
const base = (0,
|
|
129595
|
+
const base = (0, import_node_fs4.realpathSync)(process.cwd());
|
|
128861
129596
|
const resolved = path8.resolve(base, rawPath);
|
|
128862
129597
|
const relative4 = path8.relative(base, resolved);
|
|
128863
129598
|
if (relative4.startsWith("..") || path8.isAbsolute(relative4)) {
|
|
128864
129599
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
128865
129600
|
}
|
|
128866
129601
|
let existingPath = resolved;
|
|
128867
|
-
while (!(0,
|
|
129602
|
+
while (!(0, import_node_fs4.existsSync)(existingPath)) {
|
|
128868
129603
|
const parent = path8.dirname(existingPath);
|
|
128869
129604
|
if (parent === existingPath) {
|
|
128870
129605
|
break;
|
|
128871
129606
|
}
|
|
128872
129607
|
existingPath = parent;
|
|
128873
129608
|
}
|
|
128874
|
-
const realExistingPath = (0,
|
|
129609
|
+
const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
|
|
128875
129610
|
const realRelative = path8.relative(base, realExistingPath);
|
|
128876
129611
|
if (realRelative.startsWith("..") || path8.isAbsolute(realRelative)) {
|
|
128877
129612
|
throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
|
|
@@ -128899,8 +129634,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128899
129634
|
ensureDir(specsDir);
|
|
128900
129635
|
ensureDir(".postman");
|
|
128901
129636
|
const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
|
|
128902
|
-
if (!(0,
|
|
128903
|
-
(0,
|
|
129637
|
+
if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
|
|
129638
|
+
(0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
|
|
128904
129639
|
}
|
|
128905
129640
|
if (inputs.generateCiWorkflow) {
|
|
128906
129641
|
const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
|
|
@@ -128947,7 +129682,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128947
129682
|
workspaceLinkEnabled: inputs.workspaceLinkEnabled,
|
|
128948
129683
|
workspaceLinkStatus: options.workspaceLinkStatus
|
|
128949
129684
|
});
|
|
128950
|
-
(0,
|
|
129685
|
+
(0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
128951
129686
|
durableWorkspaceId,
|
|
128952
129687
|
manifestCollections,
|
|
128953
129688
|
envUids,
|
|
@@ -128955,10 +129690,11 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
128955
129690
|
discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
128956
129691
|
mappedSpecCloudKey,
|
|
128957
129692
|
inputs.specId || void 0,
|
|
128958
|
-
options.existingSpecs
|
|
129693
|
+
options.existingSpecs,
|
|
129694
|
+
options.priorState
|
|
128959
129695
|
));
|
|
128960
129696
|
if (mappedSpec && Object.keys(manifestCollections).length > 0) {
|
|
128961
|
-
(0,
|
|
129697
|
+
(0, import_node_fs4.writeFileSync)(
|
|
128962
129698
|
".postman/workflows.yaml",
|
|
128963
129699
|
buildSpecCollectionWorkflowManifest(
|
|
128964
129700
|
mappedSpec.configRelativePath,
|
|
@@ -129003,19 +129739,29 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
|
|
|
129003
129739
|
const dir = parts.slice(0, -1).join("/");
|
|
129004
129740
|
ensureDir(dir);
|
|
129005
129741
|
}
|
|
129006
|
-
(0,
|
|
129742
|
+
(0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
|
|
129743
|
+
if (inputs.provider === "github" || inputs.provider === "unknown") {
|
|
129744
|
+
const gcPath = ".github/workflows/postman-preview-gc.yml";
|
|
129745
|
+
if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
|
|
129746
|
+
ensureDir(".github/workflows");
|
|
129747
|
+
(0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
|
|
129748
|
+
}
|
|
129749
|
+
}
|
|
129007
129750
|
}
|
|
129008
129751
|
if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
|
|
129009
129752
|
return { commitSha: "", resolvedCurrentRef: "", pushed: false };
|
|
129010
129753
|
}
|
|
129011
129754
|
const provisionPath = ".github/workflows/provision.yml";
|
|
129012
|
-
const provisionExists = inputs.provider === "github" && (0,
|
|
129755
|
+
const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
|
|
129756
|
+
const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
|
|
129757
|
+
const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
|
|
129013
129758
|
const stagePaths = [
|
|
129014
129759
|
inputs.artifactDir,
|
|
129015
129760
|
".postman",
|
|
129016
129761
|
inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
|
|
129762
|
+
gcExists ? gcWorkflowPath : null,
|
|
129017
129763
|
provisionExists ? provisionPath : null
|
|
129018
|
-
].filter((entry) => typeof entry === "string" && ((0,
|
|
129764
|
+
].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
|
|
129019
129765
|
if (stagePaths.length === 0) {
|
|
129020
129766
|
dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
|
|
129021
129767
|
return {
|
|
@@ -129063,6 +129809,31 @@ async function runRepoSync(inputs, dependencies) {
|
|
|
129063
129809
|
}
|
|
129064
129810
|
}
|
|
129065
129811
|
async function runRepoSyncInner(inputs, dependencies) {
|
|
129812
|
+
const branchDecision = decideBranchTier(inputs);
|
|
129813
|
+
assertBranchAssetIds(inputs, branchDecision);
|
|
129814
|
+
const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
|
|
129815
|
+
if (!isCanonicalWriter) {
|
|
129816
|
+
if (branchDecision.tier === "preview" && branchDecision.identity.headBranch) {
|
|
129817
|
+
inputs = {
|
|
129818
|
+
...inputs,
|
|
129819
|
+
projectName: previewAssetName(inputs.projectName, branchDecision.identity.headBranch),
|
|
129820
|
+
workspaceLinkEnabled: false,
|
|
129821
|
+
environmentSyncEnabled: false,
|
|
129822
|
+
repoWriteMode: "none"
|
|
129823
|
+
};
|
|
129824
|
+
} else if (branchDecision.tier === "channel" && branchDecision.channel) {
|
|
129825
|
+
inputs = {
|
|
129826
|
+
...inputs,
|
|
129827
|
+
projectName: channelAssetName(inputs.projectName, branchDecision.channel.code),
|
|
129828
|
+
workspaceLinkEnabled: false,
|
|
129829
|
+
environmentSyncEnabled: false,
|
|
129830
|
+
repoWriteMode: "none"
|
|
129831
|
+
};
|
|
129832
|
+
}
|
|
129833
|
+
dependencies.core.info(
|
|
129834
|
+
`branch-aware sync: ${branchDecision.tier} run \u2014 branch-scoped asset set "${inputs.projectName}", no workspace repo-link mutation, no state write-back`
|
|
129835
|
+
);
|
|
129836
|
+
}
|
|
129066
129837
|
const outputs = createOutputs(inputs);
|
|
129067
129838
|
const versionRequested = inputs.collectionSyncMode === "version" || inputs.specSyncMode === "version";
|
|
129068
129839
|
const releaseLabel = deriveReleaseLabel(inputs);
|
|
@@ -129070,7 +129841,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129070
129841
|
throw new Error("release-label is required when collection-sync-mode or spec-sync-mode is version");
|
|
129071
129842
|
}
|
|
129072
129843
|
const assetProjectName = createAssetProjectName(inputs, releaseLabel);
|
|
129073
|
-
const
|
|
129844
|
+
const trackedState = readResourcesState();
|
|
129845
|
+
const resourcesState = isCanonicalWriter ? trackedState : trackedState?.workspace ? { workspace: trackedState.workspace } : null;
|
|
129074
129846
|
if (resourcesState) {
|
|
129075
129847
|
if (!inputs.workspaceId && resourcesState.workspace?.id) {
|
|
129076
129848
|
inputs.workspaceId = resourcesState.workspace.id;
|
|
@@ -129096,7 +129868,8 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129096
129868
|
}
|
|
129097
129869
|
}
|
|
129098
129870
|
}
|
|
129099
|
-
const
|
|
129871
|
+
const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
|
|
129872
|
+
const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
|
|
129100
129873
|
outputs["environment-uids-json"] = JSON.stringify(envUids);
|
|
129101
129874
|
if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
|
|
129102
129875
|
const associations = Object.entries(envUids).map(([envName, envUid]) => ({
|
|
@@ -129152,11 +129925,23 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129152
129925
|
}
|
|
129153
129926
|
}
|
|
129154
129927
|
if (!resolvedMockUrl) {
|
|
129155
|
-
const mock = await
|
|
129156
|
-
|
|
129157
|
-
|
|
129158
|
-
|
|
129159
|
-
|
|
129928
|
+
const mock = await retry(
|
|
129929
|
+
() => dependencies.postman.createMock(
|
|
129930
|
+
inputs.workspaceId,
|
|
129931
|
+
mockName,
|
|
129932
|
+
inputs.baselineCollectionId,
|
|
129933
|
+
mockEnvUid
|
|
129934
|
+
),
|
|
129935
|
+
{
|
|
129936
|
+
maxAttempts: 3,
|
|
129937
|
+
delayMs: 2e3,
|
|
129938
|
+
backoffMultiplier: 2,
|
|
129939
|
+
onRetry: ({ attempt, error: error2 }) => {
|
|
129940
|
+
dependencies.core.warning(
|
|
129941
|
+
`Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
|
|
129942
|
+
);
|
|
129943
|
+
}
|
|
129944
|
+
}
|
|
129160
129945
|
);
|
|
129161
129946
|
resolvedMockUrl = mock.url;
|
|
129162
129947
|
dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
|
|
@@ -129225,16 +130010,19 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129225
130010
|
);
|
|
129226
130011
|
} catch (error2) {
|
|
129227
130012
|
outputs["workspace-link-status"] = "failed";
|
|
129228
|
-
|
|
129229
|
-
|
|
129230
|
-
|
|
130013
|
+
const message = `Workspace link failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
130014
|
+
if (branchDecision.tier === "canonical") {
|
|
130015
|
+
throw new Error(message, { cause: error2 });
|
|
130016
|
+
}
|
|
130017
|
+
dependencies.core.warning(message);
|
|
129231
130018
|
}
|
|
129232
130019
|
}
|
|
129233
130020
|
await exportArtifacts(inputs, dependencies, envUids, assetProjectName, {
|
|
129234
130021
|
workspaceLinkStatus: outputs["workspace-link-status"],
|
|
129235
130022
|
priorWorkspaceId: resourcesState?.workspace?.id,
|
|
129236
130023
|
existingSpecs: resourcesState?.cloudResources?.specs,
|
|
129237
|
-
releaseLabel
|
|
130024
|
+
releaseLabel,
|
|
130025
|
+
priorState: resourcesState
|
|
129238
130026
|
});
|
|
129239
130027
|
const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
|
|
129240
130028
|
outputs["commit-sha"] = commit.commitSha;
|
|
@@ -129242,6 +130030,38 @@ async function runRepoSyncInner(inputs, dependencies) {
|
|
|
129242
130030
|
outputs["resolved-current-ref"] = commit.resolvedCurrentRef;
|
|
129243
130031
|
}
|
|
129244
130032
|
outputs["repo-sync-summary-json"] = createRepoSummary(outputs, envUids, commit.pushed);
|
|
130033
|
+
if (branchDecision.tier === "canonical" && inputs.specId && inputs.specContentChanged !== false && dependencies.postman.tagSpecVersion) {
|
|
130034
|
+
const shortSha = (branchDecision.identity.headSha ?? "").slice(0, 7);
|
|
130035
|
+
const tagName = inputs.releaseLabel ? `${inputs.releaseLabel}${shortSha ? ` (${shortSha})` : ""}` : shortSha || `sync-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
130036
|
+
try {
|
|
130037
|
+
const tag = await dependencies.postman.tagSpecVersion(inputs.specId, tagName);
|
|
130038
|
+
outputs["spec-version-tag"] = tag.name || tagName;
|
|
130039
|
+
if (tag.id) {
|
|
130040
|
+
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)}`;
|
|
130041
|
+
}
|
|
130042
|
+
dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
|
|
130043
|
+
} catch (error2) {
|
|
130044
|
+
const status = error2.status;
|
|
130045
|
+
if (status === 409) {
|
|
130046
|
+
const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
|
|
130047
|
+
const latest = tags[0];
|
|
130048
|
+
if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
|
|
130049
|
+
outputs["spec-version-tag"] = latest.name;
|
|
130050
|
+
outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
|
|
130051
|
+
dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
|
|
130052
|
+
} else {
|
|
130053
|
+
dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
|
|
130054
|
+
}
|
|
130055
|
+
} else {
|
|
130056
|
+
dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
130057
|
+
}
|
|
130058
|
+
}
|
|
130059
|
+
}
|
|
130060
|
+
if (inputs.branchStrategy !== "legacy" || process.env[BRANCH_DECISION_ENV]) {
|
|
130061
|
+
const decision = decideBranchTier(inputs);
|
|
130062
|
+
outputs["sync-status"] = "synced";
|
|
130063
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
130064
|
+
}
|
|
129245
130065
|
for (const [name, value] of Object.entries(outputs)) {
|
|
129246
130066
|
dependencies.core.setOutput(name, value);
|
|
129247
130067
|
}
|
|
@@ -129345,6 +130165,8 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
|
|
|
129345
130165
|
const gateway = new AccessTokenGatewayClient({
|
|
129346
130166
|
tokenProvider,
|
|
129347
130167
|
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
130168
|
+
// No fallback on the org-mode probe: the expected non-org 400 must
|
|
130169
|
+
// surface verbatim, not be re-fired against the /_api alias.
|
|
129348
130170
|
secretMasker: masker
|
|
129349
130171
|
});
|
|
129350
130172
|
const squads = await gateway.getSquads(teamId);
|
|
@@ -129400,6 +130222,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129400
130222
|
const gateway = new AccessTokenGatewayClient({
|
|
129401
130223
|
tokenProvider,
|
|
129402
130224
|
bifrostBaseUrl: inputs.postmanBifrostBase,
|
|
130225
|
+
fallbackBaseUrl: inputs.postmanFallbackBase,
|
|
129403
130226
|
teamId: resolved.teamId,
|
|
129404
130227
|
orgMode: inputs.orgMode,
|
|
129405
130228
|
secretMasker: masker
|
|
@@ -129431,7 +130254,19 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129431
130254
|
listMonitors: gatewayAssets.listMonitors.bind(gatewayAssets),
|
|
129432
130255
|
monitorExists: gatewayAssets.monitorExists.bind(gatewayAssets),
|
|
129433
130256
|
findMonitorByCollection: gatewayAssets.findMonitorByCollection.bind(gatewayAssets),
|
|
129434
|
-
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets)
|
|
130257
|
+
runMonitor: gatewayAssets.runMonitor.bind(gatewayAssets),
|
|
130258
|
+
listEnvironments: gatewayAssets.listEnvironments.bind(gatewayAssets),
|
|
130259
|
+
// GC path (preview/channel retention machine — lib/repo/preview-gc.ts).
|
|
130260
|
+
deleteEnvironment: gatewayAssets.deleteEnvironment.bind(gatewayAssets),
|
|
130261
|
+
deleteMock: gatewayAssets.deleteMock.bind(gatewayAssets),
|
|
130262
|
+
deleteMonitor: gatewayAssets.deleteMonitor.bind(gatewayAssets),
|
|
130263
|
+
deleteCollection: gatewayAssets.deleteCollection.bind(gatewayAssets),
|
|
130264
|
+
listSpecifications: gatewayAssets.listSpecifications.bind(gatewayAssets),
|
|
130265
|
+
getSpecContent: gatewayAssets.getSpecContent.bind(gatewayAssets),
|
|
130266
|
+
listSpecCollections: gatewayAssets.listSpecCollections.bind(gatewayAssets),
|
|
130267
|
+
deleteSpec: gatewayAssets.deleteSpec.bind(gatewayAssets),
|
|
130268
|
+
tagSpecVersion: gatewayAssets.tagSpecVersion.bind(gatewayAssets),
|
|
130269
|
+
listSpecVersionTags: gatewayAssets.listSpecVersionTags.bind(gatewayAssets)
|
|
129435
130270
|
};
|
|
129436
130271
|
const repoMutation = repository && (inputs.repoWriteMode === "commit-only" || inputs.repoWriteMode === "commit-and-push") ? new RepoMutationService({
|
|
129437
130272
|
provider: inputs.provider,
|
|
@@ -129468,8 +130303,43 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
|
|
|
129468
130303
|
repoMutation
|
|
129469
130304
|
};
|
|
129470
130305
|
}
|
|
130306
|
+
function decideBranchTier(inputs, env = process.env) {
|
|
130307
|
+
return resolveEffectiveBranchDecision(
|
|
130308
|
+
{
|
|
130309
|
+
strategy: inputs.branchStrategy,
|
|
130310
|
+
identity: resolveBranchIdentity(env, { defaultBranch: inputs.canonicalBranch }),
|
|
130311
|
+
canonicalBranch: inputs.canonicalBranch,
|
|
130312
|
+
channels: parseChannelRules(inputs.channels)
|
|
130313
|
+
},
|
|
130314
|
+
env
|
|
130315
|
+
);
|
|
130316
|
+
}
|
|
130317
|
+
function runGatedSkip(inputs, decision, actionCore) {
|
|
130318
|
+
actionCore.info(
|
|
130319
|
+
`branch-aware sync: gated run (${decision.reason}) \u2014 repo-sync skipped, zero workspace writes`
|
|
130320
|
+
);
|
|
130321
|
+
const outputs = createOutputs(inputs);
|
|
130322
|
+
outputs["sync-status"] = "skipped-branch-gate";
|
|
130323
|
+
outputs["branch-decision"] = serializeBranchDecision(decision);
|
|
130324
|
+
outputs["repo-sync-summary-json"] = JSON.stringify({
|
|
130325
|
+
status: "skipped-branch-gate",
|
|
130326
|
+
reason: decision.reason
|
|
130327
|
+
});
|
|
130328
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
130329
|
+
actionCore.setOutput(name, value);
|
|
130330
|
+
}
|
|
130331
|
+
return outputs;
|
|
130332
|
+
}
|
|
129471
130333
|
async function runAction(actionCore = core_exports, actionExec = exec_exports) {
|
|
129472
130334
|
const inputs = readActionInputs(actionCore);
|
|
130335
|
+
const branchDecision = decideBranchTier(inputs);
|
|
130336
|
+
if (branchDecision.tier === "gated") {
|
|
130337
|
+
return runGatedSkip(inputs, branchDecision, actionCore);
|
|
130338
|
+
}
|
|
130339
|
+
if (branchDecision.tier !== "legacy") {
|
|
130340
|
+
actionCore.info(`branch-aware sync: tier=${branchDecision.tier} (${branchDecision.reason})`);
|
|
130341
|
+
process.env[BRANCH_DECISION_ENV] = serializeBranchDecision(branchDecision);
|
|
130342
|
+
}
|
|
129473
130343
|
await mintAccessTokenIfNeeded(inputs, {
|
|
129474
130344
|
info: (message) => actionCore.info(message),
|
|
129475
130345
|
warning: (message) => actionCore.warning(message)
|
|
@@ -129526,14 +130396,19 @@ async function runAction(actionCore = core_exports, actionExec = exec_exports) {
|
|
|
129526
130396
|
}
|
|
129527
130397
|
// Annotate the CommonJS export names for ESM import in node:
|
|
129528
130398
|
0 && (module.exports = {
|
|
130399
|
+
StateUnreadableError,
|
|
130400
|
+
assertBranchAssetIds,
|
|
129529
130401
|
assertPathWithinCwd,
|
|
130402
|
+
buildBranchAssetMarker,
|
|
129530
130403
|
createRepoSyncDependencies,
|
|
130404
|
+
decideBranchTier,
|
|
129531
130405
|
getInput,
|
|
129532
130406
|
hasInput,
|
|
129533
130407
|
readActionInputs,
|
|
129534
130408
|
resolveInputs,
|
|
129535
130409
|
resolvePostmanApiKeyAndTeamId,
|
|
129536
130410
|
runAction,
|
|
130411
|
+
runGatedSkip,
|
|
129537
130412
|
runRepoSync
|
|
129538
130413
|
});
|
|
129539
130414
|
/*! Bundled license information:
|