@postman-cs/onboarding-repo-sync 2.11.2 → 3.0.1
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 +3 -3
- package/dist/action.cjs +372 -75
- package/dist/cli.cjs +372 -75
- package/dist/index.cjs +372 -75
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -118067,6 +118067,102 @@ function resolvePostmanEndpointProfile(stack, region = "us", env = process.env)
|
|
|
118067
118067
|
};
|
|
118068
118068
|
}
|
|
118069
118069
|
|
|
118070
|
+
// src/lib/postman/environment-yaml.ts
|
|
118071
|
+
var import_fs_utils = __toESM(require_dist5(), 1);
|
|
118072
|
+
var EXTENSION = ".environment.yaml";
|
|
118073
|
+
var SEPARATOR = " - ";
|
|
118074
|
+
var MAX_BASENAME_BYTES = 64;
|
|
118075
|
+
var SANITIZE_OPTIONS = {
|
|
118076
|
+
replacement: "-",
|
|
118077
|
+
combineConsecutiveReplacements: true
|
|
118078
|
+
};
|
|
118079
|
+
function assertWellFormed(value, label) {
|
|
118080
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
118081
|
+
const current2 = value.charCodeAt(index);
|
|
118082
|
+
if (current2 >= 55296 && current2 <= 56319) {
|
|
118083
|
+
const next = value.charCodeAt(index + 1);
|
|
118084
|
+
if (next < 56320 || next > 57343) throw new Error(`${label} must contain well-formed Unicode`);
|
|
118085
|
+
index += 1;
|
|
118086
|
+
} else if (current2 >= 56320 && current2 <= 57343) {
|
|
118087
|
+
throw new Error(`${label} must contain well-formed Unicode`);
|
|
118088
|
+
}
|
|
118089
|
+
}
|
|
118090
|
+
}
|
|
118091
|
+
function safeEnvironmentName(value) {
|
|
118092
|
+
assertWellFormed(value, "environment name");
|
|
118093
|
+
const hasInvalidCharacter = [...value].some((char) => {
|
|
118094
|
+
const code = char.charCodeAt(0);
|
|
118095
|
+
return char === "/" || char === "\\" || code <= 31 || code >= 127 && code <= 159;
|
|
118096
|
+
});
|
|
118097
|
+
if (!value || hasInvalidCharacter) {
|
|
118098
|
+
throw new Error("environment name must be non-empty and contain no path separators or control characters");
|
|
118099
|
+
}
|
|
118100
|
+
return (0, import_fs_utils.sanitizeFilename)(value, {
|
|
118101
|
+
...SANITIZE_OPTIONS,
|
|
118102
|
+
maxLength: MAX_BASENAME_BYTES - Buffer.byteLength(SEPARATOR)
|
|
118103
|
+
});
|
|
118104
|
+
}
|
|
118105
|
+
function environmentFileName(projectName, environmentName) {
|
|
118106
|
+
assertWellFormed(projectName, "project name");
|
|
118107
|
+
const suffix = safeEnvironmentName(environmentName);
|
|
118108
|
+
const projectBudget = Math.max(
|
|
118109
|
+
0,
|
|
118110
|
+
MAX_BASENAME_BYTES - Buffer.byteLength(SEPARATOR) - Buffer.byteLength(suffix)
|
|
118111
|
+
);
|
|
118112
|
+
const prefix = (0, import_fs_utils.sanitizeFilename)(projectName, { ...SANITIZE_OPTIONS, maxLength: projectBudget });
|
|
118113
|
+
return `${prefix ? `${prefix}${SEPARATOR}` : ""}${suffix}${EXTENSION}`;
|
|
118114
|
+
}
|
|
118115
|
+
function assertUniqueEnvironmentFileNames(projectName, environmentNames) {
|
|
118116
|
+
const owners = /* @__PURE__ */ new Map();
|
|
118117
|
+
for (const environmentName of environmentNames) {
|
|
118118
|
+
const fileName = environmentFileName(projectName, environmentName);
|
|
118119
|
+
const key = fileName.normalize("NFD").toUpperCase().toLowerCase();
|
|
118120
|
+
const owner = owners.get(key);
|
|
118121
|
+
if (owner) {
|
|
118122
|
+
throw new Error(
|
|
118123
|
+
`Environment identities "${owner}" and "${environmentName}" resolve to the same artifact filename ${fileName}`
|
|
118124
|
+
);
|
|
118125
|
+
}
|
|
118126
|
+
owners.set(key, environmentName);
|
|
118127
|
+
}
|
|
118128
|
+
}
|
|
118129
|
+
function environmentManifestRef(artifactDir, projectName, environmentName) {
|
|
118130
|
+
return `../${artifactDir}/environments/${environmentFileName(projectName, environmentName)}`;
|
|
118131
|
+
}
|
|
118132
|
+
function legacyEnvironmentManifestRef(artifactDir, environmentName) {
|
|
118133
|
+
return `../${artifactDir}/environments/${environmentName}.postman_environment.json`;
|
|
118134
|
+
}
|
|
118135
|
+
function serializeEnvironmentYaml(payload, displayName) {
|
|
118136
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
118137
|
+
throw new Error("sync-service environment must contain an object body");
|
|
118138
|
+
}
|
|
118139
|
+
const body = payload;
|
|
118140
|
+
if (!Array.isArray(body.values)) {
|
|
118141
|
+
throw new Error("sync-service environment must contain a values array");
|
|
118142
|
+
}
|
|
118143
|
+
const values = body.values.map((entry) => {
|
|
118144
|
+
const input = entry && typeof entry === "object" && !Array.isArray(entry) ? entry : {};
|
|
118145
|
+
const secret = input.secret === true || input.type === "secret";
|
|
118146
|
+
const output = {
|
|
118147
|
+
key: input.key == null ? "" : String(input.key)
|
|
118148
|
+
};
|
|
118149
|
+
if (secret) {
|
|
118150
|
+
output.secret = true;
|
|
118151
|
+
if (input.source != null) output.source = input.source;
|
|
118152
|
+
} else {
|
|
118153
|
+
output.value = input.value == null ? "" : String(input.value);
|
|
118154
|
+
}
|
|
118155
|
+
if (input.enabled === false) output.disabled = true;
|
|
118156
|
+
if (input.description != null) output.description = String(input.description);
|
|
118157
|
+
return output;
|
|
118158
|
+
});
|
|
118159
|
+
const environment = { name: displayName, values };
|
|
118160
|
+
if (Number.isInteger(body.color) && Number(body.color) >= 0 && Number(body.color) < 360) {
|
|
118161
|
+
environment.color = Number(body.color);
|
|
118162
|
+
}
|
|
118163
|
+
return dump(environment, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
|
|
118164
|
+
}
|
|
118165
|
+
|
|
118070
118166
|
// src/lib/ci-workflow-template.ts
|
|
118071
118167
|
var DEFAULT_POSTMAN_CLI_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliInstallUrl;
|
|
118072
118168
|
var DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliWindowsInstallUrl;
|
|
@@ -118094,6 +118190,16 @@ function resolveCiRunnerOs(runnerOsOption) {
|
|
|
118094
118190
|
}
|
|
118095
118191
|
throw new Error("ci-runner-os must be one of: linux, windows; got: " + runnerOs);
|
|
118096
118192
|
}
|
|
118193
|
+
function expectedEnvironmentFileName(projectName) {
|
|
118194
|
+
const name = String(projectName || "").trim();
|
|
118195
|
+
return name ? environmentFileName(name, "prod") : "";
|
|
118196
|
+
}
|
|
118197
|
+
function rubyStringLiteral(value) {
|
|
118198
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
118199
|
+
}
|
|
118200
|
+
function powershellStringLiteral(value) {
|
|
118201
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
118202
|
+
}
|
|
118097
118203
|
function renderCiWorkflowTemplate(options = {}) {
|
|
118098
118204
|
const runnerOs = resolveCiRunnerOs(options.runnerOs);
|
|
118099
118205
|
if (runnerOs === "windows") {
|
|
@@ -118102,9 +118208,15 @@ function renderCiWorkflowTemplate(options = {}) {
|
|
|
118102
118208
|
const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
|
|
118103
118209
|
const installUrl = validateHttpsInstallUrl(rawUrl);
|
|
118104
118210
|
const postmanRegion = resolvePostmanRegion(options.postmanRegion);
|
|
118105
|
-
return buildCiWorkflowLines(
|
|
118211
|
+
return buildCiWorkflowLines(
|
|
118212
|
+
installUrl,
|
|
118213
|
+
postmanRegion,
|
|
118214
|
+
options.privateMockAuth === true,
|
|
118215
|
+
options.projectName
|
|
118216
|
+
).join("\n");
|
|
118106
118217
|
}
|
|
118107
|
-
function buildCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) {
|
|
118218
|
+
function buildCiWorkflowLines(installUrl, postmanRegion, privateMockAuth, projectName) {
|
|
118219
|
+
const expectedEnvironment = expectedEnvironmentFileName(projectName);
|
|
118108
118220
|
return [
|
|
118109
118221
|
"name: CI/CD Pipeline",
|
|
118110
118222
|
"on:",
|
|
@@ -118142,11 +118254,18 @@ function buildCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) {
|
|
|
118142
118254
|
" environments = cloud.fetch('environments', {})",
|
|
118143
118255
|
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
118144
118256
|
" contract = collections.find { |path, _| path.include?('[Contract]') }&.last",
|
|
118145
|
-
|
|
118257
|
+
` expected_environment = ${rubyStringLiteral(expectedEnvironment)}`,
|
|
118258
|
+
" environment = environments.find { |path, _| File.basename(path) == expected_environment }&.last unless expected_environment.empty?",
|
|
118259
|
+
" environment ||= environments.find { |path, _| File.basename(path) == 'prod.postman_environment.json' }&.last",
|
|
118260
|
+
// A repo whose environments are named only `stage` or `dev` has no prod file to
|
|
118261
|
+
// match, and aborting there would break generated CI that worked before the YAML
|
|
118262
|
+
// migration. One environment is unambiguous, so use it; two or more without a
|
|
118263
|
+
// prod match is a real ambiguity and still aborts.
|
|
118264
|
+
" environment ||= environments.values.first if environments.size == 1",
|
|
118146
118265
|
" missing = []",
|
|
118147
|
-
" missing << '
|
|
118148
|
-
" missing << '
|
|
118149
|
-
" missing << 'environment' unless environment",
|
|
118266
|
+
" missing << '[Smoke] collection' unless smoke",
|
|
118267
|
+
" missing << '[Contract] collection' unless contract",
|
|
118268
|
+
" missing << (expected_environment.empty? ? 'environment' : expected_environment) unless environment",
|
|
118150
118269
|
` abort("Missing Postman resource IDs in .postman/resources.yaml: #{missing.join(', ')}") unless missing.empty?`,
|
|
118151
118270
|
" File.open(ENV.fetch('GITHUB_ENV'), 'a') do |file|",
|
|
118152
118271
|
' file.puts("POSTMAN_SMOKE_COLLECTION_UID=#{smoke}")',
|
|
@@ -118256,7 +118375,8 @@ function buildAdoWindowsCollectionRunLines(displayName, collectionEnvironmentNam
|
|
|
118256
118375
|
...privateMockAuth ? [" POSTMAN_API_KEY: $(POSTMAN_API_KEY)"] : []
|
|
118257
118376
|
];
|
|
118258
118377
|
}
|
|
118259
|
-
function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) {
|
|
118378
|
+
function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAuth, projectName) {
|
|
118379
|
+
const expectedEnvironment = expectedEnvironmentFileName(projectName);
|
|
118260
118380
|
return [
|
|
118261
118381
|
"trigger:",
|
|
118262
118382
|
" branches:",
|
|
@@ -118301,22 +118421,38 @@ function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAu
|
|
|
118301
118421
|
" $contract = ''",
|
|
118302
118422
|
" $environment = ''",
|
|
118303
118423
|
" $fallbackEnvironment = ''",
|
|
118424
|
+
" $environmentCount = 0",
|
|
118425
|
+
` $expectedEnvironment = ${powershellStringLiteral(expectedEnvironment)}`,
|
|
118304
118426
|
" foreach ($line in Get-Content -LiteralPath '.postman/resources.yaml') {",
|
|
118305
|
-
" if ($line -match '^ (collections|environments):\\s*$') { $section = $Matches[1]; continue }",
|
|
118427
|
+
" if ($line -match '^ (collections|environments|specs):\\s*$') { $section = $Matches[1]; continue }",
|
|
118306
118428
|
" if ($line -notmatch '^ (.+?):\\s+(.+?)\\s*$') { continue }",
|
|
118307
|
-
|
|
118429
|
+
" $rawKey = $Matches[1].Trim()",
|
|
118430
|
+
` if ($rawKey.StartsWith("'") -and $rawKey.EndsWith("'")) {`,
|
|
118431
|
+
` $key = $rawKey.Substring(1, $rawKey.Length - 2) -replace "''", "'"`,
|
|
118432
|
+
` } elseif ($rawKey.StartsWith('"') -and $rawKey.EndsWith('"')) {`,
|
|
118433
|
+
" $key = $rawKey.Substring(1, $rawKey.Length - 2)",
|
|
118434
|
+
" } else {",
|
|
118435
|
+
" $key = $rawKey",
|
|
118436
|
+
" }",
|
|
118308
118437
|
` $value = $Matches[2].Trim().Trim("'").Trim('"')`,
|
|
118309
118438
|
" if ($section -eq 'collections' -and $key -match '\\[Smoke\\]') { $smoke = $value }",
|
|
118310
118439
|
" if ($section -eq 'collections' -and $key -match '\\[Contract\\]') { $contract = $value }",
|
|
118311
118440
|
" if ($section -eq 'environments') {",
|
|
118441
|
+
" $environmentCount += 1",
|
|
118312
118442
|
" if ([string]::IsNullOrWhiteSpace($fallbackEnvironment)) { $fallbackEnvironment = $value }",
|
|
118313
|
-
"
|
|
118443
|
+
" $fileName = [IO.Path]::GetFileName($key)",
|
|
118444
|
+
" if ($fileName -eq $expectedEnvironment) { $environment = $value }",
|
|
118445
|
+
" if ([string]::IsNullOrWhiteSpace($environment) -and $fileName -eq 'prod.postman_environment.json') { $environment = $value }",
|
|
118314
118446
|
" }",
|
|
118315
118447
|
" }",
|
|
118316
|
-
|
|
118317
|
-
|
|
118318
|
-
" if ([string]::IsNullOrWhiteSpace($
|
|
118319
|
-
"
|
|
118448
|
+
// Matches the Ruby resolvers: a single environment is unambiguous, so use it
|
|
118449
|
+
// rather than aborting a pipeline that worked before the YAML migration.
|
|
118450
|
+
" if ([string]::IsNullOrWhiteSpace($environment) -and $environmentCount -eq 1) { $environment = $fallbackEnvironment }",
|
|
118451
|
+
" $missing = @()",
|
|
118452
|
+
" if ([string]::IsNullOrWhiteSpace($smoke)) { $missing += '[Smoke] collection' }",
|
|
118453
|
+
" if ([string]::IsNullOrWhiteSpace($contract)) { $missing += '[Contract] collection' }",
|
|
118454
|
+
" if ([string]::IsNullOrWhiteSpace($environment)) { $missing += $(if ($expectedEnvironment) { $expectedEnvironment } else { 'environment' }) }",
|
|
118455
|
+
" if ($missing.Count) { throw ('Missing Postman resource IDs in .postman/resources.yaml: ' + ($missing -join ', ')) }",
|
|
118320
118456
|
' Write-Host "##vso[task.setvariable variable=POSTMAN_SMOKE_COLLECTION_UID]$smoke"',
|
|
118321
118457
|
' Write-Host "##vso[task.setvariable variable=POSTMAN_CONTRACT_COLLECTION_UID]$contract"',
|
|
118322
118458
|
' Write-Host "##vso[task.setvariable variable=POSTMAN_ENVIRONMENT_UID]$environment"',
|
|
@@ -118346,7 +118482,8 @@ function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAu
|
|
|
118346
118482
|
""
|
|
118347
118483
|
];
|
|
118348
118484
|
}
|
|
118349
|
-
function buildAdoCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) {
|
|
118485
|
+
function buildAdoCiWorkflowLines(installUrl, postmanRegion, privateMockAuth, projectName) {
|
|
118486
|
+
const expectedEnvironment = expectedEnvironmentFileName(projectName);
|
|
118350
118487
|
return [
|
|
118351
118488
|
"trigger:",
|
|
118352
118489
|
" branches:",
|
|
@@ -118373,16 +118510,29 @@ function buildAdoCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) {
|
|
|
118373
118510
|
" env:",
|
|
118374
118511
|
" POSTMAN_API_KEY: $(POSTMAN_API_KEY)",
|
|
118375
118512
|
" - script: |",
|
|
118376
|
-
"
|
|
118377
|
-
"
|
|
118378
|
-
"
|
|
118379
|
-
"
|
|
118380
|
-
|
|
118381
|
-
|
|
118382
|
-
|
|
118383
|
-
|
|
118384
|
-
|
|
118385
|
-
|
|
118513
|
+
" ruby <<'RUBY'",
|
|
118514
|
+
" require 'yaml'",
|
|
118515
|
+
" config = YAML.load_file('.postman/resources.yaml') || {}",
|
|
118516
|
+
" cloud = config.fetch('canonical', config.fetch('cloudResources', {}))",
|
|
118517
|
+
" collections = cloud.fetch('collections', {})",
|
|
118518
|
+
" environments = cloud.fetch('environments', {})",
|
|
118519
|
+
" smoke = collections.find { |path, _| path.include?('[Smoke]') }&.last",
|
|
118520
|
+
" contract = collections.find { |path, _| path.include?('[Contract]') }&.last",
|
|
118521
|
+
` expected_environment = ${rubyStringLiteral(expectedEnvironment)}`,
|
|
118522
|
+
" environment = environments.find { |path, _| File.basename(path) == expected_environment }&.last unless expected_environment.empty?",
|
|
118523
|
+
" environment ||= environments.find { |path, _| File.basename(path) == 'prod.postman_environment.json' }&.last",
|
|
118524
|
+
// See the GitHub resolver: one environment is unambiguous, two or more without a
|
|
118525
|
+
// prod match still aborts.
|
|
118526
|
+
" environment ||= environments.values.first if environments.size == 1",
|
|
118527
|
+
" missing = []",
|
|
118528
|
+
" missing << '[Smoke] collection' unless smoke",
|
|
118529
|
+
" missing << '[Contract] collection' unless contract",
|
|
118530
|
+
" missing << (expected_environment.empty? ? 'environment' : expected_environment) unless environment",
|
|
118531
|
+
` abort("Missing Postman resource IDs in .postman/resources.yaml: #{missing.join(', ')}") unless missing.empty?`,
|
|
118532
|
+
' puts "##vso[task.setvariable variable=POSTMAN_SMOKE_COLLECTION_UID]#{smoke}"',
|
|
118533
|
+
' puts "##vso[task.setvariable variable=POSTMAN_CONTRACT_COLLECTION_UID]#{contract}"',
|
|
118534
|
+
' puts "##vso[task.setvariable variable=POSTMAN_ENVIRONMENT_UID]#{environment}"',
|
|
118535
|
+
" RUBY",
|
|
118386
118536
|
" displayName: Resolve Postman Resource IDs",
|
|
118387
118537
|
" - script: |",
|
|
118388
118538
|
' mkdir -p "$(Agent.TempDirectory)/postman-ssl"',
|
|
@@ -118538,7 +118688,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
|
|
|
118538
118688
|
const postmanRegion = resolvePostmanRegion(options.postmanRegion);
|
|
118539
118689
|
const installUrl = validateHttpsInstallUrl(rawUrl);
|
|
118540
118690
|
const privateMockAuth = options.privateMockAuth === true;
|
|
118541
|
-
return (runnerOs === "windows" ? buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAuth) : buildAdoCiWorkflowLines(installUrl, postmanRegion, privateMockAuth)).join("\n");
|
|
118691
|
+
return (runnerOs === "windows" ? buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion, privateMockAuth, options.projectName) : buildAdoCiWorkflowLines(installUrl, postmanRegion, privateMockAuth, options.projectName)).join("\n");
|
|
118542
118692
|
}
|
|
118543
118693
|
return renderCiWorkflowTemplate(options);
|
|
118544
118694
|
}
|
|
@@ -130051,16 +130201,71 @@ function matchesCollectionDirectory(filePath, directoryName) {
|
|
|
130051
130201
|
function matchesBaselineCollectionResource(filePath, assetProjectName) {
|
|
130052
130202
|
return matchesCollectionDirectory(filePath, assetProjectName) || matchesCollectionDirectory(filePath, `${LEGACY_BASELINE_COLLECTION_PREFIX} ${assetProjectName}`);
|
|
130053
130203
|
}
|
|
130054
|
-
function
|
|
130204
|
+
function getEnvironmentOwnershipFromResources(resourcesState, artifactDir, projectName, environmentNames) {
|
|
130205
|
+
const result = {};
|
|
130206
|
+
for (const environmentName of environmentNames) {
|
|
130207
|
+
result[environmentName] = {
|
|
130208
|
+
currentRef: canonicalizeRelativePath(environmentManifestRef(artifactDir, projectName, environmentName)),
|
|
130209
|
+
legacyRef: canonicalizeRelativePath(legacyEnvironmentManifestRef(artifactDir, environmentName))
|
|
130210
|
+
};
|
|
130211
|
+
}
|
|
130055
130212
|
const cloudEnvironments = resourcesState?.cloudResources?.environments;
|
|
130056
130213
|
if (!cloudEnvironments) {
|
|
130057
|
-
return
|
|
130214
|
+
return result;
|
|
130058
130215
|
}
|
|
130059
|
-
|
|
130060
|
-
|
|
130061
|
-
|
|
130062
|
-
|
|
130063
|
-
|
|
130216
|
+
const normalizedEntries = Object.entries(cloudEnvironments).map(([filePath, uid]) => [
|
|
130217
|
+
canonicalizeRelativePath(filePath),
|
|
130218
|
+
uid
|
|
130219
|
+
]);
|
|
130220
|
+
for (const environmentName of environmentNames) {
|
|
130221
|
+
const ownership = result[environmentName];
|
|
130222
|
+
const currentUids = new Set(
|
|
130223
|
+
normalizedEntries.filter(([filePath]) => filePath === ownership.currentRef).map(([, uid]) => uid)
|
|
130224
|
+
);
|
|
130225
|
+
const legacyUids = new Set(
|
|
130226
|
+
normalizedEntries.filter(([filePath]) => filePath === ownership.legacyRef).map(([, uid]) => uid)
|
|
130227
|
+
);
|
|
130228
|
+
if (currentUids.size > 1 || legacyUids.size > 1) {
|
|
130229
|
+
throw new StateUnreadableError(
|
|
130230
|
+
`.postman/resources.yaml maps environment "${environmentName}" artifact to multiple UIDs. Reconcile the entries before rerunning.`
|
|
130231
|
+
);
|
|
130232
|
+
}
|
|
130233
|
+
ownership.currentUid = [...currentUids][0];
|
|
130234
|
+
ownership.legacyUid = [...legacyUids][0];
|
|
130235
|
+
const uids = new Set([ownership.currentUid, ownership.legacyUid].filter(Boolean));
|
|
130236
|
+
if (uids.size > 1) {
|
|
130237
|
+
throw new StateUnreadableError(
|
|
130238
|
+
`.postman/resources.yaml maps canonical and legacy artifacts for environment "${environmentName}" to different UIDs. Reconcile the entries before rerunning.`
|
|
130239
|
+
);
|
|
130240
|
+
}
|
|
130241
|
+
}
|
|
130242
|
+
return result;
|
|
130243
|
+
}
|
|
130244
|
+
function getEnvironmentUidsFromOwnership(ownership) {
|
|
130245
|
+
const result = {};
|
|
130246
|
+
for (const [environmentName, entry] of Object.entries(ownership)) {
|
|
130247
|
+
const uid = entry.currentUid || entry.legacyUid;
|
|
130248
|
+
if (uid) result[environmentName] = uid;
|
|
130249
|
+
}
|
|
130250
|
+
return result;
|
|
130251
|
+
}
|
|
130252
|
+
function assertEnvironmentTargetAvailable(options) {
|
|
130253
|
+
assertPathWithinCwd(options.filePath, "environment target");
|
|
130254
|
+
let stats;
|
|
130255
|
+
try {
|
|
130256
|
+
stats = (0, import_node_fs6.lstatSync)(options.filePath);
|
|
130257
|
+
} catch (error2) {
|
|
130258
|
+
if (error2.code === "ENOENT") return;
|
|
130259
|
+
throw error2;
|
|
130260
|
+
}
|
|
130261
|
+
if (stats.isSymbolicLink() || !stats.isFile()) {
|
|
130262
|
+
throw new StateUnreadableError(
|
|
130263
|
+
`${options.filePath} exists but is not a regular owned environment file. Remove it before rerunning; no cloud environment was changed.`
|
|
130264
|
+
);
|
|
130265
|
+
}
|
|
130266
|
+
if (options.currentUid) return;
|
|
130267
|
+
throw new StateUnreadableError(
|
|
130268
|
+
`${options.filePath} exists but is not owned by .postman/resources.yaml. Adopt its UID into the manifest or remove the file before rerunning; no cloud environment was changed.`
|
|
130064
130269
|
);
|
|
130065
130270
|
}
|
|
130066
130271
|
function normalizeToPosix(filePath) {
|
|
@@ -130485,12 +130690,34 @@ async function persistSslSecrets(inputs, actionCore, _actionExec, repository, en
|
|
|
130485
130690
|
}
|
|
130486
130691
|
}
|
|
130487
130692
|
async function upsertEnvironments(inputs, dependencies, resourcesState, assetMarker) {
|
|
130693
|
+
const environmentNames = /* @__PURE__ */ new Set([
|
|
130694
|
+
...inputs.environments,
|
|
130695
|
+
...Object.keys(inputs.environmentUids)
|
|
130696
|
+
]);
|
|
130697
|
+
assertUniqueEnvironmentFileNames(inputs.projectName, environmentNames);
|
|
130698
|
+
const trackedOwnership = getEnvironmentOwnershipFromResources(
|
|
130699
|
+
resourcesState,
|
|
130700
|
+
inputs.artifactDir,
|
|
130701
|
+
inputs.projectName,
|
|
130702
|
+
environmentNames
|
|
130703
|
+
);
|
|
130704
|
+
const trackedUids = getEnvironmentUidsFromOwnership(trackedOwnership);
|
|
130705
|
+
for (const environmentName of environmentNames) {
|
|
130706
|
+
assertEnvironmentTargetAvailable({
|
|
130707
|
+
filePath: path9.join(
|
|
130708
|
+
inputs.artifactDir,
|
|
130709
|
+
"environments",
|
|
130710
|
+
environmentFileName(inputs.projectName, environmentName)
|
|
130711
|
+
),
|
|
130712
|
+
currentUid: trackedOwnership[environmentName].currentUid
|
|
130713
|
+
});
|
|
130714
|
+
}
|
|
130488
130715
|
const envUids = {
|
|
130489
|
-
...
|
|
130716
|
+
...trackedUids,
|
|
130490
130717
|
...inputs.environmentUids
|
|
130491
130718
|
};
|
|
130492
130719
|
if (!inputs.workspaceId) {
|
|
130493
|
-
return envUids;
|
|
130720
|
+
return { envUids, ownership: trackedOwnership };
|
|
130494
130721
|
}
|
|
130495
130722
|
const mask = resolveRepoSyncMasker(dependencies);
|
|
130496
130723
|
const envRemediation = "verify access-token/team/workspace permissions then rerun";
|
|
@@ -130592,7 +130819,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
|
|
|
130592
130819
|
);
|
|
130593
130820
|
}
|
|
130594
130821
|
}
|
|
130595
|
-
return envUids;
|
|
130822
|
+
return { envUids, ownership: trackedOwnership };
|
|
130596
130823
|
}
|
|
130597
130824
|
async function upsertMockEnvironment(inputs, dependencies, assetProjectName, mockUrl, privateMockAuth) {
|
|
130598
130825
|
if (!inputs.mockEnvironmentEnabled || !inputs.workspaceId || !mockUrl) {
|
|
@@ -130675,7 +130902,8 @@ function getCollectionDirectoryName(kind, projectName) {
|
|
|
130675
130902
|
var VOLATILE_KEYS = /* @__PURE__ */ new Set([
|
|
130676
130903
|
"createdAt",
|
|
130677
130904
|
"updatedAt",
|
|
130678
|
-
"lastUpdatedBy"
|
|
130905
|
+
"lastUpdatedBy",
|
|
130906
|
+
"lastRevision"
|
|
130679
130907
|
]);
|
|
130680
130908
|
function stripVolatileFields(obj) {
|
|
130681
130909
|
if (Array.isArray(obj)) {
|
|
@@ -130752,7 +130980,21 @@ function resolveDurableWorkspaceId(options) {
|
|
|
130752
130980
|
}
|
|
130753
130981
|
return prior === candidate ? prior : void 0;
|
|
130754
130982
|
}
|
|
130755
|
-
function buildResourcesManifest(
|
|
130983
|
+
function buildResourcesManifest(options) {
|
|
130984
|
+
const {
|
|
130985
|
+
workspaceId,
|
|
130986
|
+
collectionMap,
|
|
130987
|
+
envMap,
|
|
130988
|
+
artifactDir,
|
|
130989
|
+
projectName,
|
|
130990
|
+
localSpecRefs,
|
|
130991
|
+
mappedSpecRef,
|
|
130992
|
+
specId,
|
|
130993
|
+
existingSpecs,
|
|
130994
|
+
priorState,
|
|
130995
|
+
preserveGeneratedAssets = false,
|
|
130996
|
+
preservePriorEnvironmentAssets = preserveGeneratedAssets
|
|
130997
|
+
} = options;
|
|
130756
130998
|
const manifest = { ...priorState ?? {} };
|
|
130757
130999
|
delete manifest.version;
|
|
130758
131000
|
delete manifest.workspace;
|
|
@@ -130769,15 +131011,26 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
|
|
|
130769
131011
|
if (collectionKeys.length > 0) {
|
|
130770
131012
|
cloudResources.collections = effectiveCollectionMap;
|
|
130771
131013
|
}
|
|
130772
|
-
const priorEnvironmentMap =
|
|
131014
|
+
const priorEnvironmentMap = preservePriorEnvironmentAssets ? { ...priorState?.cloudResources?.environments ?? {} } : {};
|
|
130773
131015
|
const envEntries = Object.entries(envMap);
|
|
131016
|
+
for (const [envName] of envEntries) {
|
|
131017
|
+
const replacedRefs = /* @__PURE__ */ new Set([
|
|
131018
|
+
canonicalizeRelativePath(environmentManifestRef(artifactDir, projectName, envName)),
|
|
131019
|
+
canonicalizeRelativePath(legacyEnvironmentManifestRef(artifactDir, envName))
|
|
131020
|
+
]);
|
|
131021
|
+
for (const filePath of Object.keys(priorEnvironmentMap)) {
|
|
131022
|
+
if (replacedRefs.has(canonicalizeRelativePath(filePath))) {
|
|
131023
|
+
delete priorEnvironmentMap[filePath];
|
|
131024
|
+
}
|
|
131025
|
+
}
|
|
131026
|
+
}
|
|
130774
131027
|
if (Object.keys(priorEnvironmentMap).length > 0 || envEntries.length > 0) {
|
|
130775
131028
|
cloudResources.environments = priorEnvironmentMap;
|
|
130776
131029
|
}
|
|
130777
131030
|
if (envEntries.length > 0) {
|
|
130778
131031
|
cloudResources.environments ??= {};
|
|
130779
131032
|
for (const [envName, envUid] of envEntries) {
|
|
130780
|
-
cloudResources.environments[
|
|
131033
|
+
cloudResources.environments[environmentManifestRef(artifactDir, projectName, envName)] = envUid;
|
|
130781
131034
|
}
|
|
130782
131035
|
}
|
|
130783
131036
|
void localSpecRefs;
|
|
@@ -131513,18 +131766,19 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
131513
131766
|
}
|
|
131514
131767
|
ensureDir(".postman");
|
|
131515
131768
|
assertPathWithinCwd(".postman/resources.yaml", "resources state target");
|
|
131516
|
-
(0, import_node_fs6.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
131517
|
-
durableWorkspaceId,
|
|
131518
|
-
{},
|
|
131519
|
-
{},
|
|
131520
|
-
inputs.artifactDir,
|
|
131521
|
-
|
|
131522
|
-
|
|
131523
|
-
|
|
131524
|
-
|
|
131525
|
-
options.
|
|
131526
|
-
|
|
131527
|
-
|
|
131769
|
+
(0, import_node_fs6.writeFileSync)(".postman/resources.yaml", buildResourcesManifest({
|
|
131770
|
+
workspaceId: durableWorkspaceId,
|
|
131771
|
+
collectionMap: {},
|
|
131772
|
+
envMap: {},
|
|
131773
|
+
artifactDir: inputs.artifactDir,
|
|
131774
|
+
projectName: inputs.projectName,
|
|
131775
|
+
localSpecRefs: discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
131776
|
+
mappedSpecRef: mappedSpecCloudKey,
|
|
131777
|
+
specId: inputs.specId || void 0,
|
|
131778
|
+
existingSpecs: preservePriorWorkspaceResources ? options.existingSpecs : void 0,
|
|
131779
|
+
priorState: options.priorState,
|
|
131780
|
+
preserveGeneratedAssets: preservePriorWorkspaceResources
|
|
131781
|
+
}));
|
|
131528
131782
|
dependencies.core.info(
|
|
131529
131783
|
"onboarding-scope=spec-only; updated only workspace/spec state in .postman/resources.yaml."
|
|
131530
131784
|
);
|
|
@@ -131590,15 +131844,31 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
131590
131844
|
}
|
|
131591
131845
|
}
|
|
131592
131846
|
const environmentSpecs = [
|
|
131593
|
-
...Object.entries(envUids).map(([envName, envUid]) =>
|
|
131594
|
-
envName
|
|
131595
|
-
|
|
131596
|
-
|
|
131597
|
-
|
|
131847
|
+
...Object.entries(envUids).map(([envName, envUid]) => {
|
|
131848
|
+
const ownership = options.environmentOwnership[envName];
|
|
131849
|
+
if (!ownership) {
|
|
131850
|
+
throw new Error(
|
|
131851
|
+
`environment "${envName}" has a UID but no manifest ownership entry; environment sync and artifact export disagree`
|
|
131852
|
+
);
|
|
131853
|
+
}
|
|
131854
|
+
return {
|
|
131855
|
+
kind: "environment",
|
|
131856
|
+
envName,
|
|
131857
|
+
envUid,
|
|
131858
|
+
filePath: `${environmentsDir}/${environmentFileName(inputs.projectName, envName)}`,
|
|
131859
|
+
currentUid: ownership.currentUid,
|
|
131860
|
+
legacyUid: ownership.legacyUid,
|
|
131861
|
+
legacyFilePath: ownership.legacyUid === envUid ? `${environmentsDir}/${envName}.postman_environment.json` : void 0
|
|
131862
|
+
};
|
|
131863
|
+
}),
|
|
131598
131864
|
...options.mockEnvironmentUid ? [{
|
|
131865
|
+
kind: "mock",
|
|
131599
131866
|
envName: "manual-validation",
|
|
131600
131867
|
envUid: options.mockEnvironmentUid,
|
|
131601
|
-
filePath: `${mocksDir}/manual-validation.postman_environment.json
|
|
131868
|
+
filePath: `${mocksDir}/manual-validation.postman_environment.json`,
|
|
131869
|
+
currentUid: void 0,
|
|
131870
|
+
legacyUid: void 0,
|
|
131871
|
+
legacyFilePath: void 0
|
|
131602
131872
|
}] : []
|
|
131603
131873
|
];
|
|
131604
131874
|
const environmentStartedAt = Date.now();
|
|
@@ -131618,26 +131888,50 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
|
|
|
131618
131888
|
`environment-artifact-acquisition count=${environmentSpecs.length} width=${ARTIFACT_ACQUISITION_WIDTH} ms=${Date.now() - environmentStartedAt} status=${environmentStatus}`
|
|
131619
131889
|
);
|
|
131620
131890
|
}
|
|
131891
|
+
const legacyEnvironmentCleanupPaths = [];
|
|
131621
131892
|
for (const [index, spec] of environmentSpecs.entries()) {
|
|
131622
|
-
|
|
131623
|
-
|
|
131893
|
+
if (spec.kind === "mock") {
|
|
131894
|
+
assertPathWithinCwd(spec.filePath, "environment target");
|
|
131895
|
+
writeJsonFile(spec.filePath, sanitizeMockEnvironmentArtifact(environmentPayloads[index]), true);
|
|
131896
|
+
continue;
|
|
131897
|
+
}
|
|
131898
|
+
assertEnvironmentTargetAvailable({
|
|
131899
|
+
filePath: spec.filePath,
|
|
131900
|
+
currentUid: spec.currentUid
|
|
131901
|
+
});
|
|
131902
|
+
(0, import_node_fs6.writeFileSync)(
|
|
131624
131903
|
spec.filePath,
|
|
131625
|
-
|
|
131626
|
-
true
|
|
131904
|
+
serializeEnvironmentYaml(environmentPayloads[index], `${inputs.projectName} - ${spec.envName}`)
|
|
131627
131905
|
);
|
|
131906
|
+
if (spec.legacyFilePath) {
|
|
131907
|
+
legacyEnvironmentCleanupPaths.push(spec.legacyFilePath);
|
|
131908
|
+
}
|
|
131628
131909
|
}
|
|
131629
131910
|
assertPathWithinCwd(".postman/resources.yaml", "resources state target");
|
|
131630
|
-
(0, import_node_fs6.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
|
|
131631
|
-
durableWorkspaceId,
|
|
131632
|
-
manifestCollections,
|
|
131633
|
-
envUids,
|
|
131634
|
-
inputs.artifactDir,
|
|
131635
|
-
|
|
131636
|
-
|
|
131637
|
-
|
|
131638
|
-
|
|
131639
|
-
options.
|
|
131640
|
-
|
|
131911
|
+
(0, import_node_fs6.writeFileSync)(".postman/resources.yaml", buildResourcesManifest({
|
|
131912
|
+
workspaceId: durableWorkspaceId,
|
|
131913
|
+
collectionMap: manifestCollections,
|
|
131914
|
+
envMap: envUids,
|
|
131915
|
+
artifactDir: inputs.artifactDir,
|
|
131916
|
+
projectName: inputs.projectName,
|
|
131917
|
+
localSpecRefs: discoveredSpecs.map((spec) => spec.configRelativePath),
|
|
131918
|
+
mappedSpecRef: mappedSpecCloudKey,
|
|
131919
|
+
specId: inputs.specId || void 0,
|
|
131920
|
+
existingSpecs: options.existingSpecs,
|
|
131921
|
+
priorState: options.priorState,
|
|
131922
|
+
// The export path always rewrites collections from this run, but keeps prior
|
|
131923
|
+
// environments so an environment tracked by an earlier run is not dropped.
|
|
131924
|
+
preserveGeneratedAssets: false,
|
|
131925
|
+
preservePriorEnvironmentAssets: preservePriorWorkspaceResources
|
|
131926
|
+
}));
|
|
131927
|
+
for (const legacyFilePath of legacyEnvironmentCleanupPaths) {
|
|
131928
|
+
assertPathWithinCwd(legacyFilePath, "legacy environment target");
|
|
131929
|
+
try {
|
|
131930
|
+
(0, import_node_fs6.unlinkSync)(legacyFilePath);
|
|
131931
|
+
} catch (error2) {
|
|
131932
|
+
if (error2.code !== "ENOENT") throw error2;
|
|
131933
|
+
}
|
|
131934
|
+
}
|
|
131641
131935
|
if (mappedSpec && Object.keys(manifestCollections).length > 0) {
|
|
131642
131936
|
let existingWorkflows;
|
|
131643
131937
|
try {
|
|
@@ -131662,6 +131956,7 @@ function renderCiWorkflow(inputs, privateMockAuth) {
|
|
|
131662
131956
|
}
|
|
131663
131957
|
if (inputs.provider === "azure-devops") {
|
|
131664
131958
|
return getCiWorkflowTemplate(inputs.provider, {
|
|
131959
|
+
projectName: inputs.projectName,
|
|
131665
131960
|
postmanCliInstallUrl: inputs.postmanCliInstallUrl,
|
|
131666
131961
|
postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
|
|
131667
131962
|
runnerOs: inputs.ciRunnerOs,
|
|
@@ -131670,6 +131965,7 @@ function renderCiWorkflow(inputs, privateMockAuth) {
|
|
|
131670
131965
|
});
|
|
131671
131966
|
}
|
|
131672
131967
|
return renderCiWorkflowTemplate({
|
|
131968
|
+
projectName: inputs.projectName,
|
|
131673
131969
|
postmanCliInstallUrl: inputs.postmanCliInstallUrl,
|
|
131674
131970
|
postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
|
|
131675
131971
|
runnerOs: inputs.ciRunnerOs,
|
|
@@ -131918,10 +132214,10 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
|
|
|
131918
132214
|
}
|
|
131919
132215
|
}
|
|
131920
132216
|
const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
|
|
131921
|
-
const envUids = onboardingScope === "full" ? await logger.phase(
|
|
132217
|
+
const { envUids, ownership: environmentOwnership } = onboardingScope === "full" ? await logger.phase(
|
|
131922
132218
|
"sync-environments",
|
|
131923
132219
|
async () => upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker)
|
|
131924
|
-
) : {};
|
|
132220
|
+
) : { envUids: {}, ownership: {} };
|
|
131925
132221
|
outputs["environment-uids-json"] = JSON.stringify(envUids);
|
|
131926
132222
|
dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
|
|
131927
132223
|
if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
|
|
@@ -132321,6 +132617,7 @@ async function runRepoSyncInner(inputs, dependencies, executionContext) {
|
|
|
132321
132617
|
mockEnvironmentUid: outputs["mock-environment-uid"] || void 0,
|
|
132322
132618
|
releaseLabel,
|
|
132323
132619
|
priorState: resourcesState,
|
|
132620
|
+
environmentOwnership,
|
|
132324
132621
|
preparedPrebuiltCollections,
|
|
132325
132622
|
privateMockAuth: outputs["mock-auth-required"] === "true"
|
|
132326
132623
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postman-cs/onboarding-repo-sync",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Postman repo sync GitHub Action.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"@actions/core": "^3.0.1",
|
|
56
56
|
"@actions/exec": "^3.0.0",
|
|
57
57
|
"@postman-cs/automation-core": "^1.8.1",
|
|
58
|
+
"@postman/fs-utils": "0.3.1",
|
|
58
59
|
"js-yaml": "^5.2.1",
|
|
59
60
|
"libsodium-wrappers": "0.8.4",
|
|
60
61
|
"undici": "^8.10.0"
|