@postman-cse/onboarding-repo-sync 2.1.8 → 2.1.9

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/dist/action.cjs CHANGED
@@ -125116,6 +125116,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125116
125116
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
125117
125117
  fallbackBaseUrl: "https://go.postman.co/_api",
125118
125118
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
125119
+ cliWindowsInstallUrl: "https://dl-cli.pstmn.io/install/win64.ps1",
125119
125120
  iapubBaseUrl: "https://iapub.postman.co"
125120
125121
  },
125121
125122
  beta: {
@@ -125123,6 +125124,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125123
125124
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
125124
125125
  fallbackBaseUrl: "https://go.postman-beta.co/_api",
125125
125126
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
125127
+ cliWindowsInstallUrl: "https://dl-cli.pstmn-beta.io/install/win64.ps1",
125126
125128
  iapubBaseUrl: "https://iapub.postman.co"
125127
125129
  }
125128
125130
  };
@@ -125156,6 +125158,7 @@ function resolvePostmanEndpointProfile(stack, region = "us") {
125156
125158
 
125157
125159
  // src/lib/ci-workflow-template.ts
125158
125160
  var DEFAULT_POSTMAN_CLI_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliInstallUrl;
125161
+ var DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliWindowsInstallUrl;
125159
125162
  function validateHttpsInstallUrl(url) {
125160
125163
  const safeUrlPattern = /^https:\/\/[A-Za-z0-9.-]+\/[A-Za-z0-9._~/?=&%-]+$/;
125161
125164
  if (!safeUrlPattern.test(url)) {
@@ -125172,7 +125175,18 @@ function resolvePostmanRegion(postmanRegionOption) {
125172
125175
  }
125173
125176
  return postmanRegion;
125174
125177
  }
125178
+ function resolveCiRunnerOs(runnerOsOption) {
125179
+ const runnerOs = String(runnerOsOption || "").trim() || "linux";
125180
+ if (runnerOs === "linux" || runnerOs === "windows") {
125181
+ return runnerOs;
125182
+ }
125183
+ throw new Error("ci-runner-os must be one of: linux, windows; got: " + runnerOs);
125184
+ }
125175
125185
  function renderCiWorkflowTemplate(options = {}) {
125186
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125187
+ if (runnerOs === "windows") {
125188
+ throw new Error("ci-runner-os=windows is currently supported for azure-devops workflows only");
125189
+ }
125176
125190
  const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125177
125191
  const installUrl = validateHttpsInstallUrl(rawUrl);
125178
125192
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
@@ -125279,6 +125293,132 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
125279
125293
  ];
125280
125294
  }
125281
125295
  var CI_WORKFLOW_TEMPLATE = renderCiWorkflowTemplate();
125296
+ function buildAdoWindowsCollectionRunLines(displayName, collectionEnvironmentName) {
125297
+ return [
125298
+ " - pwsh: |",
125299
+ " $ErrorActionPreference = 'Stop'",
125300
+ " function Resolve-AdoOptional([string]$Value) {",
125301
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125302
+ " return $Value",
125303
+ " }",
125304
+ ` $collectionUid = $env:${collectionEnvironmentName}`,
125305
+ " $ciEnvironment = Resolve-AdoOptional $env:CI_ENVIRONMENT",
125306
+ " if ([string]::IsNullOrWhiteSpace($ciEnvironment)) { $ciEnvironment = 'Production' }",
125307
+ ` $arguments = @('collection', 'run', $collectionUid, '-e', $env:POSTMAN_ENVIRONMENT_UID, '--report-events', '--env-var', "CI_ENVIRONMENT=$ciEnvironment")`,
125308
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125309
+ " $clientCert = Join-Path $sslRoot 'client.crt'",
125310
+ " $clientKey = Join-Path $sslRoot 'client.key'",
125311
+ " $caCert = Join-Path $sslRoot 'ca.crt'",
125312
+ " if (Test-Path -LiteralPath $clientCert) {",
125313
+ " $arguments += @('--ssl-client-cert', $clientCert, '--ssl-client-key', $clientKey)",
125314
+ " $passphrase = Resolve-AdoOptional $env:POSTMAN_SSL_CLIENT_PASSPHRASE",
125315
+ " if (-not [string]::IsNullOrWhiteSpace($passphrase)) {",
125316
+ " $arguments += @('--ssl-client-passphrase', $passphrase)",
125317
+ " }",
125318
+ " if (Test-Path -LiteralPath $caCert) {",
125319
+ " $arguments += @('--ssl-extra-ca-certs', $caCert)",
125320
+ " }",
125321
+ " }",
125322
+ " & postman @arguments",
125323
+ ` if ($LASTEXITCODE -ne 0) { throw '${displayName} failed with exit code ' + $LASTEXITCODE }`,
125324
+ ` displayName: ${displayName}`,
125325
+ " env:",
125326
+ ` ${collectionEnvironmentName}: $(${collectionEnvironmentName})`,
125327
+ " POSTMAN_ENVIRONMENT_UID: $(POSTMAN_ENVIRONMENT_UID)",
125328
+ " CI_ENVIRONMENT: $(CI_ENVIRONMENT)",
125329
+ " POSTMAN_SSL_CLIENT_PASSPHRASE: $(POSTMAN_SSL_CLIENT_PASSPHRASE)"
125330
+ ];
125331
+ }
125332
+ function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) {
125333
+ return [
125334
+ "trigger:",
125335
+ " branches:",
125336
+ " include:",
125337
+ " - main",
125338
+ "schedules:",
125339
+ ' - cron: "0 */6 * * *"',
125340
+ " displayName: Scheduled run",
125341
+ " branches:",
125342
+ " include:",
125343
+ " - main",
125344
+ " always: true",
125345
+ "pool:",
125346
+ " vmImage: windows-latest",
125347
+ "steps:",
125348
+ " - checkout: self",
125349
+ " persistCredentials: true",
125350
+ " - pwsh: |",
125351
+ " $ErrorActionPreference = 'Stop'",
125352
+ " if (-not (Get-Command postman -ErrorAction SilentlyContinue)) {",
125353
+ " [System.Net.ServicePointManager]::SecurityProtocol = 3072",
125354
+ " Invoke-Expression ((New-Object System.Net.WebClient).DownloadString($env:POSTMAN_CLI_INSTALL_URL))",
125355
+ " }",
125356
+ " & postman --version",
125357
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI installation failed' }",
125358
+ " displayName: Install Postman CLI",
125359
+ " env:",
125360
+ ` POSTMAN_CLI_INSTALL_URL: ${installUrl}`,
125361
+ " - pwsh: |",
125362
+ " $ErrorActionPreference = 'Stop'",
125363
+ " $arguments = @('login', '--with-api-key', $env:POSTMAN_API_KEY)",
125364
+ ...postmanRegion === "eu" ? [" $arguments += @('--region', 'eu')"] : [],
125365
+ " & postman @arguments",
125366
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI login failed' }",
125367
+ " displayName: Login to Postman CLI",
125368
+ " env:",
125369
+ " POSTMAN_API_KEY: $(POSTMAN_API_KEY)",
125370
+ " - pwsh: |",
125371
+ " $ErrorActionPreference = 'Stop'",
125372
+ " $section = ''",
125373
+ " $smoke = ''",
125374
+ " $contract = ''",
125375
+ " $environment = ''",
125376
+ " $fallbackEnvironment = ''",
125377
+ " foreach ($line in Get-Content -LiteralPath '.postman/resources.yaml') {",
125378
+ " if ($line -match '^ (collections|environments):\\s*$') { $section = $Matches[1]; continue }",
125379
+ " if ($line -notmatch '^ (.+?):\\s+(.+?)\\s*$') { continue }",
125380
+ ` $key = $Matches[1].Trim().Trim("'").Trim('"')`,
125381
+ ` $value = $Matches[2].Trim().Trim("'").Trim('"')`,
125382
+ " if ($section -eq 'collections' -and $key -match '\\[Smoke\\]') { $smoke = $value }",
125383
+ " if ($section -eq 'collections' -and $key -match '\\[Contract\\]') { $contract = $value }",
125384
+ " if ($section -eq 'environments') {",
125385
+ " if ([string]::IsNullOrWhiteSpace($fallbackEnvironment)) { $fallbackEnvironment = $value }",
125386
+ " if ($key -match 'prod\\.postman_environment\\.json$') { $environment = $value }",
125387
+ " }",
125388
+ " }",
125389
+ " if ([string]::IsNullOrWhiteSpace($environment)) { $environment = $fallbackEnvironment }",
125390
+ " if ([string]::IsNullOrWhiteSpace($smoke)) { throw 'Missing smoke collection UID in .postman/resources.yaml' }",
125391
+ " if ([string]::IsNullOrWhiteSpace($contract)) { throw 'Missing contract collection UID in .postman/resources.yaml' }",
125392
+ " if ([string]::IsNullOrWhiteSpace($environment)) { throw 'Missing environment UID in .postman/resources.yaml' }",
125393
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_SMOKE_COLLECTION_UID]$smoke"',
125394
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_CONTRACT_COLLECTION_UID]$contract"',
125395
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_ENVIRONMENT_UID]$environment"',
125396
+ " displayName: Resolve Postman Resource IDs",
125397
+ " - pwsh: |",
125398
+ " $ErrorActionPreference = 'Stop'",
125399
+ " function Resolve-AdoOptional([string]$Value) {",
125400
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125401
+ " return $Value",
125402
+ " }",
125403
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125404
+ " New-Item -ItemType Directory -Path $sslRoot -Force | Out-Null",
125405
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.crt'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_CERT_B64))",
125406
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.key'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_KEY_B64))",
125407
+ " $extraCa = Resolve-AdoOptional $env:POSTMAN_SSL_EXTRA_CA_CERTS_B64",
125408
+ " if (-not [string]::IsNullOrWhiteSpace($extraCa)) {",
125409
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'ca.crt'), [Convert]::FromBase64String($extraCa))",
125410
+ " }",
125411
+ " condition: ne(variables['POSTMAN_SSL_CLIENT_CERT_B64'], '')",
125412
+ " displayName: Decode SSL certificates",
125413
+ " env:",
125414
+ " POSTMAN_SSL_CLIENT_CERT_B64: $(POSTMAN_SSL_CLIENT_CERT_B64)",
125415
+ " POSTMAN_SSL_CLIENT_KEY_B64: $(POSTMAN_SSL_CLIENT_KEY_B64)",
125416
+ " POSTMAN_SSL_EXTRA_CA_CERTS_B64: $(POSTMAN_SSL_EXTRA_CA_CERTS_B64)",
125417
+ ...buildAdoWindowsCollectionRunLines("Run Smoke Tests", "POSTMAN_SMOKE_COLLECTION_UID"),
125418
+ ...buildAdoWindowsCollectionRunLines("Run Contract Tests", "POSTMAN_CONTRACT_COLLECTION_UID"),
125419
+ ""
125420
+ ];
125421
+ }
125282
125422
  function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
125283
125423
  return [
125284
125424
  "trigger:",
@@ -125458,10 +125598,12 @@ function renderGcWorkflowTemplate() {
125458
125598
  }
125459
125599
  var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
125460
125600
  function getCiWorkflowTemplate(provider, options = {}) {
125601
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125461
125602
  if (provider === "azure-devops") {
125462
- const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125603
+ const rawUrl = runnerOs === "windows" ? String(options.postmanCliWindowsInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL : String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125463
125604
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
125464
- return buildAdoCiWorkflowLines(validateHttpsInstallUrl(rawUrl), postmanRegion).join("\n");
125605
+ const installUrl = validateHttpsInstallUrl(rawUrl);
125606
+ return (runnerOs === "windows" ? buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) : buildAdoCiWorkflowLines(installUrl, postmanRegion)).join("\n");
125465
125607
  }
125466
125608
  return renderCiWorkflowTemplate(options);
125467
125609
  }
@@ -126856,6 +126998,17 @@ var BifrostInternalIntegrationAdapter = class {
126856
126998
  mask: this.secretMasker
126857
126999
  };
126858
127000
  }
127001
+ /**
127002
+ * One-line non-fatal preflight reason: operation + entity (repo/path) + cause +
127003
+ * operator action. Secrets are redacted via secretMasker, then CR/LF and other
127004
+ * line-breaking whitespace are collapsed to spaces so CI logs stay one line.
127005
+ * Display-only: does not alter request repoUrl/path or probe classification.
127006
+ */
127007
+ unknownFilesystemLookupReason(repoUrl, fsPath, cause) {
127008
+ return this.secretMasker(
127009
+ `filesystem lookup for repository ${repoUrl} path ${fsPath} ${cause}; verify Bifrost connectivity/credentials then rerun`
127010
+ ).replace(/[\r\n\v\f\u2028\u2029]+/g, " ");
127011
+ }
126859
127012
  async associateSystemEnvironments(workspaceId, associations) {
126860
127013
  if (associations.length === 0) {
126861
127014
  return;
@@ -126926,7 +127079,11 @@ var BifrostInternalIntegrationAdapter = class {
126926
127079
  } catch {
126927
127080
  return {
126928
127081
  state: "unknown",
126929
- reason: `filesystem lookup returned non-JSON body (HTTP ${response.status})`
127082
+ reason: this.unknownFilesystemLookupReason(
127083
+ repoUrl,
127084
+ fsPath,
127085
+ `returned non-JSON body (HTTP ${response.status})`
127086
+ )
126930
127087
  };
126931
127088
  }
126932
127089
  const errorMetaWorkspaceId = typeof parsed?.error?.meta?.workspaceId === "string" && parsed.error.meta.workspaceId.trim() ? parsed.error.meta.workspaceId.trim() : void 0;
@@ -126941,7 +127098,11 @@ var BifrostInternalIntegrationAdapter = class {
126941
127098
  if (!id) {
126942
127099
  return {
126943
127100
  state: "unknown",
126944
- reason: "filesystem lookup returned 200 without a workspace id"
127101
+ reason: this.unknownFilesystemLookupReason(
127102
+ repoUrl,
127103
+ fsPath,
127104
+ "returned 200 without a workspace id"
127105
+ )
126945
127106
  };
126946
127107
  }
126947
127108
  const name = typeof parsed.data.name === "string" && parsed.data.name.trim() ? parsed.data.name.trim() : void 0;
@@ -126959,17 +127120,30 @@ var BifrostInternalIntegrationAdapter = class {
126959
127120
  }
126960
127121
  return {
126961
127122
  state: "unknown",
126962
- reason: "filesystem lookup returned 403 without error.meta.workspaceId"
127123
+ reason: this.unknownFilesystemLookupReason(
127124
+ repoUrl,
127125
+ fsPath,
127126
+ "returned 403 without error.meta.workspaceId"
127127
+ )
126963
127128
  };
126964
127129
  }
126965
127130
  return {
126966
127131
  state: "unknown",
126967
- reason: `filesystem lookup returned HTTP ${response.status}`
127132
+ reason: this.unknownFilesystemLookupReason(
127133
+ repoUrl,
127134
+ fsPath,
127135
+ `returned HTTP ${response.status}`
127136
+ )
126968
127137
  };
126969
127138
  } catch (error2) {
127139
+ const cause = error2 instanceof Error ? error2.message : String(error2);
126970
127140
  return {
126971
127141
  state: "unknown",
126972
- reason: error2 instanceof Error ? error2.message : String(error2)
127142
+ reason: this.unknownFilesystemLookupReason(
127143
+ repoUrl,
127144
+ fsPath,
127145
+ `failed: ${cause}`
127146
+ )
126973
127147
  };
126974
127148
  }
126975
127149
  }
@@ -127183,6 +127357,12 @@ var postmanRepoSyncActionContract = {
127183
127357
  required: false,
127184
127358
  default: ".github/workflows/ci.yml"
127185
127359
  },
127360
+ "ci-runner-os": {
127361
+ description: "Runner operating system for the generated CI workflow.",
127362
+ required: false,
127363
+ default: "linux",
127364
+ allowedValues: ["linux", "windows"]
127365
+ },
127186
127366
  "project-name": {
127187
127367
  description: "Service project name used for environment, mock, and monitor naming.",
127188
127368
  required: true
@@ -128894,6 +129074,25 @@ function parseAssetMarker(description) {
128894
129074
  }
128895
129075
 
128896
129076
  // src/index.ts
129077
+ var identitySecretMasker = (input) => input;
129078
+ function resolveRepoSyncMasker(dependencies) {
129079
+ return dependencies.secretMasker ?? identitySecretMasker;
129080
+ }
129081
+ function causeText(cause) {
129082
+ return cause instanceof Error ? cause.message : String(cause);
129083
+ }
129084
+ function toOneLineDisplay(value) {
129085
+ return String(value ?? "").replace(/[\r\n\u0085\u2028\u2029\v\f]+/g, " ").replace(/[ \t]{2,}/g, " ").trim();
129086
+ }
129087
+ function formatOrchestrationIssue(params) {
129088
+ const operation = toOneLineDisplay(params.mask(params.operation));
129089
+ const entity = toOneLineDisplay(params.mask(params.entity));
129090
+ const maskedCause = toOneLineDisplay(params.mask(causeText(params.cause)));
129091
+ const detail = toOneLineDisplay(params.mask(params.detail ?? ""));
129092
+ const remediation = toOneLineDisplay(params.mask(params.remediation));
129093
+ const body = `${operation} failed for ${entity}: ${maskedCause}${detail}`.replace(/[.]+$/u, "");
129094
+ return toOneLineDisplay(`${body}. ${remediation}`);
129095
+ }
128897
129096
  function parseBooleanInput(value, defaultValue) {
128898
129097
  const normalized = String(value || "").trim().toLowerCase();
128899
129098
  if (!normalized) return defaultValue;
@@ -128993,6 +129192,17 @@ function parseBranchStrategy(value) {
128993
129192
  `Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
128994
129193
  );
128995
129194
  }
129195
+ function parseCiRunnerOs(value) {
129196
+ const definition = postmanRepoSyncActionContract.inputs["ci-runner-os"];
129197
+ const allowed = definition.allowedValues ?? [];
129198
+ const normalized = String(value || "").trim() || (definition.default ?? "linux");
129199
+ if (allowed.includes(normalized)) {
129200
+ return normalized;
129201
+ }
129202
+ throw new Error(
129203
+ `Unsupported ci-runner-os "${normalized}". Supported values: ${allowed.join(", ")}`
129204
+ );
129205
+ }
128996
129206
  function normalizeReleaseLabel(value) {
128997
129207
  const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
128998
129208
  return cleaned;
@@ -129070,6 +129280,7 @@ function resolveInputs(env = process.env) {
129070
129280
  provider: repoContext.provider,
129071
129281
  ciWorkflowBase64: getInput2("ci-workflow-base64", env),
129072
129282
  generateCiWorkflow: parseBooleanInput(getInput2("generate-ci-workflow", env), true),
129283
+ ciRunnerOs: parseCiRunnerOs(getInput2("ci-runner-os", env)),
129073
129284
  monitorType: getInput2("monitor-type", env) || "cloud",
129074
129285
  ciWorkflowPath: getInput2("ci-workflow-path", env) || (repoContext.provider === "azure-devops" ? "azure-pipelines.yml" : ".github/workflows/ci.yml"),
129075
129286
  orgMode: parseBooleanInput(getInput2("org-mode", env), false),
@@ -129088,6 +129299,7 @@ function resolveInputs(env = process.env) {
129088
129299
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129089
129300
  postmanFallbackBase: endpointProfile.fallbackBaseUrl,
129090
129301
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
129302
+ postmanCliWindowsInstallUrl: endpointProfile.cliWindowsInstallUrl,
129091
129303
  postmanIapubBase: endpointProfile.iapubBaseUrl
129092
129304
  };
129093
129305
  }
@@ -129357,6 +129569,7 @@ function readActionInputs(actionCore) {
129357
129569
  INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
129358
129570
  INPUT_CI_WORKFLOW_BASE64: readInput(actionCore, "ci-workflow-base64"),
129359
129571
  INPUT_GENERATE_CI_WORKFLOW: readInput(actionCore, "generate-ci-workflow"),
129572
+ INPUT_CI_RUNNER_OS: readInput(actionCore, "ci-runner-os"),
129360
129573
  INPUT_MONITOR_TYPE: readInput(actionCore, "monitor-type") || "cloud",
129361
129574
  INPUT_CI_WORKFLOW_PATH: readInput(actionCore, "ci-workflow-path"),
129362
129575
  INPUT_ORG_MODE: readInput(actionCore, "org-mode"),
@@ -129474,19 +129687,34 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129474
129687
  if (!inputs.workspaceId) {
129475
129688
  return envUids;
129476
129689
  }
129690
+ const mask = resolveRepoSyncMasker(dependencies);
129691
+ const envRemediation = "verify access-token/team/workspace permissions then rerun";
129477
129692
  for (const envName of inputs.environments) {
129478
129693
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
129479
129694
  const displayName = `${inputs.projectName} - ${envName}`;
129480
129695
  let existingUid = String(envUids[envName] || "").trim();
129481
129696
  if (!existingUid) {
129482
- const discovered = await dependencies.postman.findEnvironmentByName(
129483
- inputs.workspaceId,
129484
- displayName
129485
- );
129486
- if (discovered?.uid) {
129487
- existingUid = discovered.uid;
129488
- dependencies.core.info(
129489
- `Discovered existing environment for ${displayName}: ${existingUid}`
129697
+ try {
129698
+ const discovered = await dependencies.postman.findEnvironmentByName(
129699
+ inputs.workspaceId,
129700
+ displayName
129701
+ );
129702
+ if (discovered?.uid) {
129703
+ existingUid = discovered.uid;
129704
+ dependencies.core.info(
129705
+ `Discovered existing environment for ${displayName}: ${existingUid}`
129706
+ );
129707
+ }
129708
+ } catch (error2) {
129709
+ throw new Error(
129710
+ formatOrchestrationIssue({
129711
+ operation: "Environment discovery",
129712
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129713
+ cause: error2,
129714
+ remediation: envRemediation,
129715
+ mask
129716
+ }),
129717
+ { cause: error2 }
129490
129718
  );
129491
129719
  }
129492
129720
  }
@@ -129506,17 +129734,43 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129506
129734
  }
129507
129735
  const values2 = buildEnvironmentValues(envName, runtimeUrl);
129508
129736
  if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
129509
- await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129737
+ try {
129738
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129739
+ } catch (error2) {
129740
+ throw new Error(
129741
+ formatOrchestrationIssue({
129742
+ operation: "Environment update",
129743
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}" (uid ${existingUid})`,
129744
+ cause: error2,
129745
+ remediation: envRemediation,
129746
+ mask
129747
+ }),
129748
+ { cause: error2 }
129749
+ );
129750
+ }
129510
129751
  envUids[envName] = existingUid;
129511
129752
  continue;
129512
129753
  }
129513
129754
  const values = buildEnvironmentValues(envName, runtimeUrl);
129514
129755
  if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
129515
- envUids[envName] = await dependencies.postman.createEnvironment(
129516
- inputs.workspaceId,
129517
- displayName,
129518
- values
129519
- );
129756
+ try {
129757
+ envUids[envName] = await dependencies.postman.createEnvironment(
129758
+ inputs.workspaceId,
129759
+ displayName,
129760
+ values
129761
+ );
129762
+ } catch (error2) {
129763
+ throw new Error(
129764
+ formatOrchestrationIssue({
129765
+ operation: "Environment create",
129766
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129767
+ cause: error2,
129768
+ remediation: envRemediation,
129769
+ mask
129770
+ }),
129771
+ { cause: error2 }
129772
+ );
129773
+ }
129520
129774
  }
129521
129775
  return envUids;
129522
129776
  }
@@ -129773,11 +130027,15 @@ function renderCiWorkflow(inputs) {
129773
130027
  if (inputs.provider === "azure-devops") {
129774
130028
  return getCiWorkflowTemplate(inputs.provider, {
129775
130029
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130030
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130031
+ runnerOs: inputs.ciRunnerOs,
129776
130032
  postmanRegion: inputs.postmanRegion
129777
130033
  });
129778
130034
  }
129779
130035
  return renderCiWorkflowTemplate({
129780
130036
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130037
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130038
+ runnerOs: inputs.ciRunnerOs,
129781
130039
  postmanRegion: inputs.postmanRegion
129782
130040
  });
129783
130041
  }
@@ -129872,6 +130130,7 @@ async function runRepoSync(inputs, dependencies) {
129872
130130
  }
129873
130131
  }
129874
130132
  async function runRepoSyncInner(inputs, dependencies) {
130133
+ const mask = resolveRepoSyncMasker(dependencies);
129875
130134
  const branchDecision = decideBranchTier(inputs);
129876
130135
  assertBranchAssetIds(inputs, branchDecision);
129877
130136
  const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
@@ -129981,8 +130240,15 @@ async function runRepoSyncInner(inputs, dependencies) {
129981
130240
  outputs["environment-sync-status"] = "success";
129982
130241
  } catch (error2) {
129983
130242
  outputs["environment-sync-status"] = "failed";
130243
+ const associationSummary = associations.map((entry) => `${entry.envUid}->${entry.systemEnvId}`).join(", ");
129984
130244
  dependencies.core.warning(
129985
- `System environment association failed: ${error2 instanceof Error ? error2.message : String(error2)}`
130245
+ formatOrchestrationIssue({
130246
+ operation: "System environment association",
130247
+ entity: `workspace ${inputs.workspaceId} (${associationSummary})`,
130248
+ cause: error2,
130249
+ remediation: "verify access-token/team/system-env mapping then rerun",
130250
+ mask
130251
+ })
129986
130252
  );
129987
130253
  }
129988
130254
  } else if (Object.keys(envUids).length > 0) {
@@ -130010,37 +130276,72 @@ async function runRepoSyncInner(inputs, dependencies) {
130010
130276
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
130011
130277
  }
130012
130278
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
130013
- const discovered = await dependencies.postman.findMockByCollection(
130014
- inputs.baselineCollectionId,
130015
- mockEnvUid,
130016
- mockName
130017
- );
130018
- if (discovered) {
130019
- resolvedMockUrl = discovered.mockUrl;
130020
- dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130279
+ try {
130280
+ const discovered = await dependencies.postman.findMockByCollection(
130281
+ inputs.baselineCollectionId,
130282
+ mockEnvUid,
130283
+ mockName
130284
+ );
130285
+ if (discovered) {
130286
+ resolvedMockUrl = discovered.mockUrl;
130287
+ dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130288
+ }
130289
+ } catch (error2) {
130290
+ throw new Error(
130291
+ formatOrchestrationIssue({
130292
+ operation: "Mock discovery",
130293
+ entity: `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`,
130294
+ cause: error2,
130295
+ remediation: "verify collection/environment access then rerun",
130296
+ mask
130297
+ }),
130298
+ { cause: error2 }
130299
+ );
130021
130300
  }
130022
130301
  }
130023
130302
  if (!resolvedMockUrl) {
130024
- const mock = await retry(
130025
- () => dependencies.postman.createMock(
130026
- inputs.workspaceId,
130027
- mockName,
130028
- inputs.baselineCollectionId,
130029
- mockEnvUid
130030
- ),
130031
- {
130032
- maxAttempts: 3,
130033
- delayMs: 2e3,
130034
- backoffMultiplier: 2,
130035
- onRetry: ({ attempt, error: error2 }) => {
130036
- dependencies.core.warning(
130037
- `Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
130038
- );
130303
+ const mockEntity = `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`;
130304
+ const mockRemediation = "verify collection/environment access then rerun";
130305
+ try {
130306
+ const mock = await retry(
130307
+ () => dependencies.postman.createMock(
130308
+ inputs.workspaceId,
130309
+ mockName,
130310
+ inputs.baselineCollectionId,
130311
+ mockEnvUid
130312
+ ),
130313
+ {
130314
+ maxAttempts: 3,
130315
+ delayMs: 2e3,
130316
+ backoffMultiplier: 2,
130317
+ onRetry: ({ attempt, maxAttempts, error: error2 }) => {
130318
+ dependencies.core.warning(
130319
+ formatOrchestrationIssue({
130320
+ operation: `Mock create attempt ${attempt}/${maxAttempts}`,
130321
+ entity: mockEntity,
130322
+ cause: error2,
130323
+ remediation: mockRemediation,
130324
+ mask,
130325
+ detail: "; retrying"
130326
+ })
130327
+ );
130328
+ }
130039
130329
  }
130040
- }
130041
- );
130042
- resolvedMockUrl = mock.url;
130043
- dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130330
+ );
130331
+ resolvedMockUrl = mock.url;
130332
+ dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130333
+ } catch (error2) {
130334
+ throw new Error(
130335
+ formatOrchestrationIssue({
130336
+ operation: "Mock create",
130337
+ entity: mockEntity,
130338
+ cause: error2,
130339
+ remediation: mockRemediation,
130340
+ mask
130341
+ }),
130342
+ { cause: error2 }
130343
+ );
130344
+ }
130044
130345
  }
130045
130346
  outputs["mock-url"] = resolvedMockUrl;
130046
130347
  }
@@ -130051,35 +130352,71 @@ async function runRepoSyncInner(inputs, dependencies) {
130051
130352
  if (monitorEnvUid && inputs.monitorType !== "cli") {
130052
130353
  let resolvedMonitorId = "";
130053
130354
  const monitorName = `${assetProjectName} - Smoke Monitor`;
130355
+ const monitorRemediation = "verify monitor IDs/access or set monitor-cron then rerun";
130054
130356
  if (inputs.monitorId) {
130055
130357
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
130056
130358
  if (valid) {
130057
130359
  resolvedMonitorId = inputs.monitorId;
130058
130360
  dependencies.core.info(`Reusing monitor from explicit input: ${resolvedMonitorId}`);
130059
130361
  } else {
130060
- dependencies.core.warning(`Explicit monitor-id ${inputs.monitorId} not found in Postman, falling through to discovery.`);
130362
+ dependencies.core.warning(
130363
+ formatOrchestrationIssue({
130364
+ operation: "Explicit monitor-id lookup",
130365
+ entity: `monitor-id ${inputs.monitorId} workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130366
+ cause: "not found in Postman",
130367
+ remediation: monitorRemediation,
130368
+ mask,
130369
+ detail: "; falling through to discovery"
130370
+ })
130371
+ );
130061
130372
  }
130062
130373
  }
130063
130374
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
130064
- const discovered = await dependencies.postman.findMonitorByCollection(
130065
- inputs.smokeCollectionId,
130066
- monitorEnvUid,
130067
- monitorName
130068
- );
130069
- if (discovered) {
130070
- resolvedMonitorId = discovered.uid;
130071
- dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130375
+ try {
130376
+ const discovered = await dependencies.postman.findMonitorByCollection(
130377
+ inputs.smokeCollectionId,
130378
+ monitorEnvUid,
130379
+ monitorName
130380
+ );
130381
+ if (discovered) {
130382
+ resolvedMonitorId = discovered.uid;
130383
+ dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130384
+ }
130385
+ } catch (error2) {
130386
+ throw new Error(
130387
+ formatOrchestrationIssue({
130388
+ operation: "Monitor discovery",
130389
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130390
+ cause: error2,
130391
+ remediation: monitorRemediation,
130392
+ mask
130393
+ }),
130394
+ { cause: error2 }
130395
+ );
130072
130396
  }
130073
130397
  }
130074
130398
  if (!resolvedMonitorId) {
130075
- resolvedMonitorId = await dependencies.postman.createMonitor(
130076
- inputs.workspaceId,
130077
- monitorName,
130078
- inputs.smokeCollectionId,
130079
- monitorEnvUid,
130080
- effectiveCron || void 0
130081
- );
130082
- dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130399
+ try {
130400
+ resolvedMonitorId = await dependencies.postman.createMonitor(
130401
+ inputs.workspaceId,
130402
+ monitorName,
130403
+ inputs.smokeCollectionId,
130404
+ monitorEnvUid,
130405
+ effectiveCron || void 0
130406
+ );
130407
+ dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130408
+ } catch (error2) {
130409
+ throw new Error(
130410
+ formatOrchestrationIssue({
130411
+ operation: "Monitor create",
130412
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130413
+ cause: error2,
130414
+ remediation: monitorRemediation,
130415
+ mask
130416
+ }),
130417
+ { cause: error2 }
130418
+ );
130419
+ }
130083
130420
  }
130084
130421
  outputs["monitor-id"] = resolvedMonitorId;
130085
130422
  if (!effectiveCron && resolvedMonitorId) {
@@ -130088,7 +130425,13 @@ async function runRepoSyncInner(inputs, dependencies) {
130088
130425
  dependencies.core.info(`Triggered one-time run for monitor: ${resolvedMonitorId}`);
130089
130426
  } catch (error2) {
130090
130427
  dependencies.core.warning(
130091
- `Failed to trigger one-time run for monitor ${resolvedMonitorId}: ${error2 instanceof Error ? error2.message : String(error2)}`
130428
+ formatOrchestrationIssue({
130429
+ operation: "Monitor one-time run",
130430
+ entity: `monitor ${resolvedMonitorId} name "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130431
+ cause: error2,
130432
+ remediation: monitorRemediation,
130433
+ mask
130434
+ })
130092
130435
  );
130093
130436
  }
130094
130437
  }
@@ -130153,18 +130496,57 @@ async function runRepoSyncInner(inputs, dependencies) {
130153
130496
  dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
130154
130497
  } catch (error2) {
130155
130498
  const status = error2.status;
130499
+ const specEntity = `spec ${inputs.specId} tag "${tagName}" workspace ${inputs.workspaceId}`;
130500
+ const specRemediation = "inspect existing tags/access and rerun";
130156
130501
  if (status === 409) {
130157
- const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
130502
+ let tags = [];
130503
+ let listError;
130504
+ if (dependencies.postman.listSpecVersionTags) {
130505
+ try {
130506
+ tags = await dependencies.postman.listSpecVersionTags(inputs.specId);
130507
+ } catch (lookupError) {
130508
+ listError = lookupError;
130509
+ }
130510
+ }
130158
130511
  const latest = tags[0];
130159
- if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130512
+ if (!listError && latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130160
130513
  outputs["spec-version-tag"] = latest.name;
130161
130514
  outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
130162
130515
  dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
130516
+ } else if (listError) {
130517
+ dependencies.core.warning(
130518
+ formatOrchestrationIssue({
130519
+ operation: "Spec Hub tagging conflict",
130520
+ entity: specEntity,
130521
+ cause: `create=${causeText(error2)}; listSpecVersionTags=${causeText(listError)}`,
130522
+ remediation: specRemediation,
130523
+ mask,
130524
+ detail: "; could not list existing tags to confirm adoption"
130525
+ })
130526
+ );
130163
130527
  } else {
130164
- dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
130528
+ dependencies.core.warning(
130529
+ formatOrchestrationIssue({
130530
+ operation: "Spec Hub tagging conflict",
130531
+ entity: specEntity,
130532
+ cause: error2,
130533
+ remediation: specRemediation,
130534
+ mask,
130535
+ detail: "; latest changelog group already carries a hand-applied or nonmatching tag; leaving it in place"
130536
+ })
130537
+ );
130165
130538
  }
130166
130539
  } else {
130167
- dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
130540
+ dependencies.core.warning(
130541
+ formatOrchestrationIssue({
130542
+ operation: "Spec version tagging",
130543
+ entity: specEntity,
130544
+ cause: error2,
130545
+ remediation: specRemediation,
130546
+ mask,
130547
+ detail: " (non-fatal)"
130548
+ })
130549
+ );
130168
130550
  }
130169
130551
  }
130170
130552
  }
@@ -130195,7 +130577,16 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130195
130577
  }
130196
130578
  } catch (error2) {
130197
130579
  if (typeof error2 === "object" && error2 !== null && "status" in error2 && (error2.status === 401 || error2.status === 403)) {
130198
- actionCore.warning("Provided postman-api-key is invalid or expired.");
130580
+ const status = error2.status;
130581
+ actionCore.warning(
130582
+ formatOrchestrationIssue({
130583
+ operation: "PMAK GET /me validation",
130584
+ entity: `status ${status}`,
130585
+ cause: error2,
130586
+ remediation: "replace postman-api-key or provide a valid postman-access-token then rerun",
130587
+ mask: masker
130588
+ })
130589
+ );
130199
130590
  } else {
130200
130591
  throw error2;
130201
130592
  }
@@ -130217,6 +130608,7 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130217
130608
  const keyName = `repo-sync-action-${Date.now()}`;
130218
130609
  apiKey = await internalIntegration.createApiKey(keyName);
130219
130610
  actionCore.setSecret(apiKey);
130611
+ const persistSecretRemediation = "grant Actions secrets write permission or set POSTMAN_API_KEY manually then rerun";
130220
130612
  if (inputs.provider === "azure-devops") {
130221
130613
  if (options.persistGeneratedApiKeySecret ?? true) {
130222
130614
  actionCore.warning(
@@ -130243,16 +130635,48 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130243
130635
  ignoreReturnCode: true
130244
130636
  });
130245
130637
  if (ghCommand.exitCode !== 0) {
130246
- actionCore.warning(`Failed to save POSTMAN_API_KEY secret: ${ghCommand.stderr}`);
130638
+ actionCore.warning(
130639
+ formatOrchestrationIssue({
130640
+ operation: "gh secret set POSTMAN_API_KEY",
130641
+ entity: `repository ${repo}`,
130642
+ cause: ghCommand.stderr || `exit code ${ghCommand.exitCode}`,
130643
+ remediation: persistSecretRemediation,
130644
+ mask: masker
130645
+ })
130646
+ );
130247
130647
  }
130248
130648
  } catch (error2) {
130249
130649
  actionCore.warning(
130250
- `Error saving POSTMAN_API_KEY secret: ${error2 instanceof Error ? error2.message : String(error2)}`
130650
+ formatOrchestrationIssue({
130651
+ operation: "gh secret set POSTMAN_API_KEY",
130652
+ entity: `repository ${repo}`,
130653
+ cause: error2,
130654
+ remediation: persistSecretRemediation,
130655
+ mask: masker
130656
+ })
130251
130657
  );
130252
130658
  }
130659
+ } else {
130660
+ actionCore.warning(
130661
+ formatOrchestrationIssue({
130662
+ operation: "gh secret set POSTMAN_API_KEY",
130663
+ entity: "repository (missing)",
130664
+ cause: "repository context is empty",
130665
+ remediation: "set repository context or persist POSTMAN_API_KEY manually then rerun",
130666
+ mask: masker
130667
+ })
130668
+ );
130253
130669
  }
130254
130670
  } else if (options.persistGeneratedApiKeySecret ?? true) {
130255
- actionCore.warning("No GitHub token provided; cannot save generated POSTMAN_API_KEY secret.");
130671
+ actionCore.warning(
130672
+ formatOrchestrationIssue({
130673
+ operation: "gh secret set POSTMAN_API_KEY",
130674
+ entity: inputs.repository ? `repository ${inputs.repository}` : "repository (unknown)",
130675
+ cause: "no GitHub token provided",
130676
+ remediation: "provide github-token/gh-fallback-token or set POSTMAN_API_KEY manually then rerun",
130677
+ mask: masker
130678
+ })
130679
+ );
130256
130680
  } else {
130257
130681
  actionCore.info("Skipping generated POSTMAN_API_KEY GitHub secret persistence for this run.");
130258
130682
  }
@@ -130291,7 +130715,13 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130291
130715
  const isNonOrgSquads = error2 instanceof HttpError && error2.status === 400 && /squad feature is not available/i.test(error2.responseBody);
130292
130716
  if (!isNonOrgSquads) {
130293
130717
  actionCore.warning(
130294
- `Org-mode auto-detection via ums squads failed (non-fatal; set org-mode explicitly if Bifrost calls fail): ${error2 instanceof Error ? error2.message : String(error2)}`
130718
+ formatOrchestrationIssue({
130719
+ operation: "Org-mode auto-detection via ums squads",
130720
+ entity: `team ${teamId}`,
130721
+ cause: error2,
130722
+ remediation: "set org-mode and team-id explicitly then rerun",
130723
+ mask: masker
130724
+ })
130295
130725
  );
130296
130726
  }
130297
130727
  }
@@ -130411,7 +130841,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130411
130841
  core: factories.core,
130412
130842
  postman,
130413
130843
  internalIntegration,
130414
- repoMutation
130844
+ repoMutation,
130845
+ secretMasker: masker
130415
130846
  };
130416
130847
  }
130417
130848
  function decideBranchTier(inputs, env = process.env) {