@postman-cse/onboarding-repo-sync 0.14.0 → 0.14.3
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 +5 -0
- package/action.yml +5 -1
- package/dist/action.cjs +6 -4
- package/dist/cli.cjs +255 -0
- package/dist/index.cjs +6 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# postman-repo-sync-action
|
|
2
2
|
|
|
3
|
+
[](https://github.com/postman-cs/postman-repo-sync-action/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/postman-cs/postman-repo-sync-action/releases)
|
|
5
|
+
[](https://www.npmjs.com/package/@postman-cse/onboarding-repo-sync)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
3
8
|
Sync Postman workspace assets into a repository and optionally connect the workspace back to that repository.
|
|
4
9
|
|
|
5
10
|
Use this action when a repo should contain the Postman artifacts needed for CI, reviews, and repeatable API test runs:
|
package/action.yml
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
name: postman-repo-sync-action
|
|
2
|
-
description: Sync exported Postman artifacts into a repository and expose
|
|
2
|
+
description: Sync exported Postman artifacts into a repository and expose workspace-link and environment-sync outputs.
|
|
3
|
+
author: Postman
|
|
4
|
+
branding:
|
|
5
|
+
icon: refresh-cw
|
|
6
|
+
color: orange
|
|
3
7
|
inputs:
|
|
4
8
|
|
|
5
9
|
generate-ci-workflow:
|
package/dist/action.cjs
CHANGED
|
@@ -24607,20 +24607,22 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl) {
|
|
|
24607
24607
|
if (!payload) {
|
|
24608
24608
|
return void 0;
|
|
24609
24609
|
}
|
|
24610
|
-
const
|
|
24611
|
-
const
|
|
24610
|
+
const root = asRecord(payload.session) ?? payload;
|
|
24611
|
+
const identity = asRecord(root.identity);
|
|
24612
|
+
const data = asRecord(root.data);
|
|
24612
24613
|
const user = asRecord(data?.user);
|
|
24613
24614
|
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
24614
24615
|
const singleRole = coerceText(user?.role);
|
|
24615
24616
|
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
24616
24617
|
const resolved = {
|
|
24617
24618
|
source: "iapub/sessions",
|
|
24618
|
-
userId: coerceId(user?.id),
|
|
24619
|
+
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
24619
24620
|
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
24620
24621
|
teamId: coerceId(identity?.team),
|
|
24622
|
+
teamName: coerceText(user?.teamName),
|
|
24621
24623
|
teamDomain: coerceText(identity?.domain),
|
|
24622
24624
|
...roles ? { roles } : {},
|
|
24623
|
-
consumerType: coerceText(
|
|
24625
|
+
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
24624
24626
|
};
|
|
24625
24627
|
memoizedSessionIdentity = resolved;
|
|
24626
24628
|
return resolved;
|
package/dist/cli.cjs
CHANGED
|
@@ -22617,10 +22617,255 @@ var HttpError = class _HttpError extends Error {
|
|
|
22617
22617
|
};
|
|
22618
22618
|
|
|
22619
22619
|
// src/lib/postman/credential-identity.ts
|
|
22620
|
+
var sessionPath = "/api/sessions/current";
|
|
22621
|
+
var pmakMemo = /* @__PURE__ */ new Map();
|
|
22622
|
+
var sessionMemo = /* @__PURE__ */ new Map();
|
|
22620
22623
|
var memoizedSessionIdentity;
|
|
22621
22624
|
function getMemoizedSessionIdentity() {
|
|
22622
22625
|
return memoizedSessionIdentity;
|
|
22623
22626
|
}
|
|
22627
|
+
function asRecord(value) {
|
|
22628
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
22629
|
+
return void 0;
|
|
22630
|
+
}
|
|
22631
|
+
return value;
|
|
22632
|
+
}
|
|
22633
|
+
function coerceId(raw) {
|
|
22634
|
+
return raw ? String(raw) : void 0;
|
|
22635
|
+
}
|
|
22636
|
+
function coerceText(raw) {
|
|
22637
|
+
if (typeof raw !== "string") {
|
|
22638
|
+
return void 0;
|
|
22639
|
+
}
|
|
22640
|
+
const trimmed = raw.trim();
|
|
22641
|
+
return trimmed ? trimmed : void 0;
|
|
22642
|
+
}
|
|
22643
|
+
function normalizeBaseUrl(raw) {
|
|
22644
|
+
return String(raw || "").replace(/\/+$/, "");
|
|
22645
|
+
}
|
|
22646
|
+
async function resolvePmakIdentity(opts) {
|
|
22647
|
+
const apiKey = String(opts.apiKey || "").trim();
|
|
22648
|
+
if (!apiKey) {
|
|
22649
|
+
return void 0;
|
|
22650
|
+
}
|
|
22651
|
+
const baseUrl = normalizeBaseUrl(opts.apiBaseUrl);
|
|
22652
|
+
const memoKey = `${baseUrl}::${apiKey}`;
|
|
22653
|
+
let pending = pmakMemo.get(memoKey);
|
|
22654
|
+
if (!pending) {
|
|
22655
|
+
pending = probePmakIdentity(baseUrl, apiKey, opts.fetchImpl ?? fetch);
|
|
22656
|
+
pmakMemo.set(memoKey, pending);
|
|
22657
|
+
}
|
|
22658
|
+
return pending;
|
|
22659
|
+
}
|
|
22660
|
+
async function probePmakIdentity(baseUrl, apiKey, fetchImpl) {
|
|
22661
|
+
try {
|
|
22662
|
+
const response = await fetchImpl(`${baseUrl}/me`, {
|
|
22663
|
+
method: "GET",
|
|
22664
|
+
headers: { "X-Api-Key": apiKey }
|
|
22665
|
+
});
|
|
22666
|
+
if (!response.ok) {
|
|
22667
|
+
return void 0;
|
|
22668
|
+
}
|
|
22669
|
+
const payload = asRecord(await response.json());
|
|
22670
|
+
const user = asRecord(payload?.user);
|
|
22671
|
+
if (!user) {
|
|
22672
|
+
return void 0;
|
|
22673
|
+
}
|
|
22674
|
+
return {
|
|
22675
|
+
source: "pmak/me",
|
|
22676
|
+
userId: coerceId(user.id),
|
|
22677
|
+
fullName: coerceText(user.fullName) ?? coerceText(user.username),
|
|
22678
|
+
teamId: coerceId(user.teamId),
|
|
22679
|
+
teamName: coerceText(user.teamName),
|
|
22680
|
+
teamDomain: coerceText(user.teamDomain)
|
|
22681
|
+
};
|
|
22682
|
+
} catch {
|
|
22683
|
+
return void 0;
|
|
22684
|
+
}
|
|
22685
|
+
}
|
|
22686
|
+
async function resolveSessionIdentity(opts) {
|
|
22687
|
+
const accessToken = String(opts.accessToken || "").trim();
|
|
22688
|
+
if (!accessToken) {
|
|
22689
|
+
return void 0;
|
|
22690
|
+
}
|
|
22691
|
+
const baseUrl = normalizeBaseUrl(opts.iapubBaseUrl);
|
|
22692
|
+
const memoKey = `${baseUrl}::${accessToken}`;
|
|
22693
|
+
let pending = sessionMemo.get(memoKey);
|
|
22694
|
+
if (!pending) {
|
|
22695
|
+
pending = probeSessionIdentity(baseUrl, accessToken, opts.fetchImpl ?? fetch);
|
|
22696
|
+
sessionMemo.set(memoKey, pending);
|
|
22697
|
+
}
|
|
22698
|
+
return pending;
|
|
22699
|
+
}
|
|
22700
|
+
async function probeSessionIdentity(baseUrl, accessToken, fetchImpl) {
|
|
22701
|
+
try {
|
|
22702
|
+
const response = await fetchImpl(`${baseUrl}${sessionPath}`, {
|
|
22703
|
+
method: "GET",
|
|
22704
|
+
headers: { "x-access-token": accessToken }
|
|
22705
|
+
});
|
|
22706
|
+
if (!response.ok) {
|
|
22707
|
+
return void 0;
|
|
22708
|
+
}
|
|
22709
|
+
const payload = asRecord(await response.json());
|
|
22710
|
+
if (!payload) {
|
|
22711
|
+
return void 0;
|
|
22712
|
+
}
|
|
22713
|
+
const root = asRecord(payload.session) ?? payload;
|
|
22714
|
+
const identity = asRecord(root.identity);
|
|
22715
|
+
const data = asRecord(root.data);
|
|
22716
|
+
const user = asRecord(data?.user);
|
|
22717
|
+
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
22718
|
+
const singleRole = coerceText(user?.role);
|
|
22719
|
+
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
22720
|
+
const resolved = {
|
|
22721
|
+
source: "iapub/sessions",
|
|
22722
|
+
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
22723
|
+
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
22724
|
+
teamId: coerceId(identity?.team),
|
|
22725
|
+
teamName: coerceText(user?.teamName),
|
|
22726
|
+
teamDomain: coerceText(identity?.domain),
|
|
22727
|
+
...roles ? { roles } : {},
|
|
22728
|
+
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
22729
|
+
};
|
|
22730
|
+
memoizedSessionIdentity = resolved;
|
|
22731
|
+
return resolved;
|
|
22732
|
+
} catch {
|
|
22733
|
+
return void 0;
|
|
22734
|
+
}
|
|
22735
|
+
}
|
|
22736
|
+
function describeTeam(id) {
|
|
22737
|
+
const label = id?.teamName ?? id?.teamDomain;
|
|
22738
|
+
return `team ${id?.teamId ?? "unresolved"}${label ? ` (${label})` : ""}`;
|
|
22739
|
+
}
|
|
22740
|
+
function formatIdentityLine(id, mask) {
|
|
22741
|
+
const teamPart = id.teamId ? describeTeam(id) : "team unresolved";
|
|
22742
|
+
const domainPart = id.teamDomain ? `, domain ${id.teamDomain}` : "";
|
|
22743
|
+
if (id.source === "pmak/me") {
|
|
22744
|
+
const userPart = id.userId ? `user ${id.userId}${id.fullName ? ` (${id.fullName})` : ""}, ` : "";
|
|
22745
|
+
return mask(`postman: PMAK identity - ${userPart}${teamPart}${domainPart}`);
|
|
22746
|
+
}
|
|
22747
|
+
return mask(
|
|
22748
|
+
`postman: access-token session identity - ${teamPart}${domainPart} [source: iapub/sessions]`
|
|
22749
|
+
);
|
|
22750
|
+
}
|
|
22751
|
+
function crossCheckIdentities(args) {
|
|
22752
|
+
if (args.mode === "off") {
|
|
22753
|
+
return { ok: true, level: "ok", message: "" };
|
|
22754
|
+
}
|
|
22755
|
+
const pmakTeamId = args.pmak?.teamId;
|
|
22756
|
+
const sessionTeamId = args.session?.teamId;
|
|
22757
|
+
if (pmakTeamId && sessionTeamId && pmakTeamId !== sessionTeamId) {
|
|
22758
|
+
const level = args.mode === "enforce" ? "fail" : "note";
|
|
22759
|
+
const lead = level === "fail" ? "credential preflight FAILED" : "credential preflight note";
|
|
22760
|
+
const fix = level === "fail" ? "Use one credential pair from a single parent org: re-mint the access token from the same parent org as postman-api-key (postman-resolve-service-token-action, or POST https://api.getpostman.com/service-account-tokens with that team's PMAK), or set postman-api-key to the matching parent org." : "Use one credential pair from a single parent org. Set credential-preflight: enforce to fail the run on this condition.";
|
|
22761
|
+
return {
|
|
22762
|
+
ok: false,
|
|
22763
|
+
level,
|
|
22764
|
+
message: args.mask(
|
|
22765
|
+
`postman: ${lead} - PMAK belongs to ${describeTeam(args.pmak)} but the access token's session belongs to a different parent org, ${describeTeam(args.session)}. Assets would be created against one team while Bifrost linking and governance act under the other, producing duplicate-link 400s and workspaces not visible to the other credential. ` + fix
|
|
22766
|
+
)
|
|
22767
|
+
};
|
|
22768
|
+
}
|
|
22769
|
+
if (pmakTeamId && sessionTeamId) {
|
|
22770
|
+
const scope = args.workspaceTeamId || args.explicitTeamId ? "parent org team" : "team";
|
|
22771
|
+
const label = args.pmak?.teamName ?? args.pmak?.teamDomain ?? args.session?.teamName ?? args.session?.teamDomain;
|
|
22772
|
+
return {
|
|
22773
|
+
ok: true,
|
|
22774
|
+
level: "ok",
|
|
22775
|
+
message: args.mask(
|
|
22776
|
+
`postman: credential preflight OK - PMAK and access token both resolve to ${scope} ${pmakTeamId}${label ? ` (${label})` : ""}`
|
|
22777
|
+
)
|
|
22778
|
+
};
|
|
22779
|
+
}
|
|
22780
|
+
const missing = [
|
|
22781
|
+
!pmakTeamId ? "PMAK identity" : void 0,
|
|
22782
|
+
!sessionTeamId ? "access-token session identity" : void 0
|
|
22783
|
+
].filter(Boolean).join(" and ");
|
|
22784
|
+
return {
|
|
22785
|
+
ok: false,
|
|
22786
|
+
level: "note",
|
|
22787
|
+
message: args.mask(
|
|
22788
|
+
`postman: credential preflight note - cross-check skipped because the ${missing} did not resolve a team id; continuing with reactive error guidance only`
|
|
22789
|
+
)
|
|
22790
|
+
};
|
|
22791
|
+
}
|
|
22792
|
+
async function runCredentialPreflight(args) {
|
|
22793
|
+
if (args.mode === "off") {
|
|
22794
|
+
return;
|
|
22795
|
+
}
|
|
22796
|
+
const mask = args.mask;
|
|
22797
|
+
const apiKey = String(args.postmanApiKey || "").trim();
|
|
22798
|
+
const accessToken = String(args.postmanAccessToken || "").trim();
|
|
22799
|
+
let pmak;
|
|
22800
|
+
if (apiKey) {
|
|
22801
|
+
try {
|
|
22802
|
+
pmak = await resolvePmakIdentity({
|
|
22803
|
+
apiBaseUrl: args.apiBaseUrl,
|
|
22804
|
+
apiKey,
|
|
22805
|
+
fetchImpl: args.fetchImpl
|
|
22806
|
+
});
|
|
22807
|
+
} catch (error) {
|
|
22808
|
+
args.log.warning(
|
|
22809
|
+
mask(
|
|
22810
|
+
`postman: credential preflight could not resolve PMAK identity: ${error instanceof Error ? error.message : String(error)}`
|
|
22811
|
+
)
|
|
22812
|
+
);
|
|
22813
|
+
}
|
|
22814
|
+
if (pmak) {
|
|
22815
|
+
args.log.info(formatIdentityLine(pmak, mask));
|
|
22816
|
+
} else {
|
|
22817
|
+
args.log.warning(
|
|
22818
|
+
mask("postman: credential preflight could not resolve PMAK identity from GET /me; continuing")
|
|
22819
|
+
);
|
|
22820
|
+
}
|
|
22821
|
+
}
|
|
22822
|
+
if (!accessToken) {
|
|
22823
|
+
args.log.info(mask("postman: Bifrost diagnostics limited: no access token"));
|
|
22824
|
+
return;
|
|
22825
|
+
}
|
|
22826
|
+
let session;
|
|
22827
|
+
try {
|
|
22828
|
+
session = await resolveSessionIdentity({
|
|
22829
|
+
iapubBaseUrl: args.iapubBaseUrl,
|
|
22830
|
+
accessToken,
|
|
22831
|
+
fetchImpl: args.fetchImpl
|
|
22832
|
+
});
|
|
22833
|
+
} catch (error) {
|
|
22834
|
+
args.log.warning(
|
|
22835
|
+
mask(
|
|
22836
|
+
`postman: credential preflight could not resolve access-token session identity: ${error instanceof Error ? error.message : String(error)}`
|
|
22837
|
+
)
|
|
22838
|
+
);
|
|
22839
|
+
}
|
|
22840
|
+
if (session) {
|
|
22841
|
+
args.log.info(formatIdentityLine(session, mask));
|
|
22842
|
+
} else {
|
|
22843
|
+
args.log.warning(
|
|
22844
|
+
mask(
|
|
22845
|
+
"postman: credential preflight could not resolve the access-token session identity from iapub; continuing with reactive error guidance only"
|
|
22846
|
+
)
|
|
22847
|
+
);
|
|
22848
|
+
}
|
|
22849
|
+
const result = crossCheckIdentities({
|
|
22850
|
+
pmak,
|
|
22851
|
+
session,
|
|
22852
|
+
workspaceTeamId: args.workspaceTeamId,
|
|
22853
|
+
explicitTeamId: args.explicitTeamId,
|
|
22854
|
+
mode: args.mode,
|
|
22855
|
+
mask
|
|
22856
|
+
});
|
|
22857
|
+
if (!result.message) {
|
|
22858
|
+
return;
|
|
22859
|
+
}
|
|
22860
|
+
if (result.level === "fail") {
|
|
22861
|
+
throw new Error(result.message);
|
|
22862
|
+
}
|
|
22863
|
+
if (result.level === "note") {
|
|
22864
|
+
args.log.warning(result.message);
|
|
22865
|
+
return;
|
|
22866
|
+
}
|
|
22867
|
+
args.log.info(result.message);
|
|
22868
|
+
}
|
|
22624
22869
|
|
|
22625
22870
|
// src/lib/postman/error-advice.ts
|
|
22626
22871
|
var WORKSPACE_PERSONAL_ONLY_ADVICE = "Workspace creation failed: This may be an Org-mode account that requires a workspace-team-id input. The Postman API does not allow creating team workspaces at the organization level. Use the workspace-team-id input to specify which sub-team should own this workspace.";
|
|
@@ -24457,6 +24702,16 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
24457
24702
|
inputs.sslClientPassphrase,
|
|
24458
24703
|
inputs.sslExtraCaCerts
|
|
24459
24704
|
]);
|
|
24705
|
+
await runCredentialPreflight({
|
|
24706
|
+
apiBaseUrl: inputs.postmanApiBase,
|
|
24707
|
+
iapubBaseUrl: inputs.postmanIapubBase,
|
|
24708
|
+
postmanApiKey: inputs.postmanApiKey,
|
|
24709
|
+
postmanAccessToken: inputs.postmanAccessToken,
|
|
24710
|
+
explicitTeamId: inputs.teamId || void 0,
|
|
24711
|
+
mode: inputs.credentialPreflight,
|
|
24712
|
+
mask: initialMasker,
|
|
24713
|
+
log: reporter
|
|
24714
|
+
});
|
|
24460
24715
|
const resolvingExec = createCliExec(initialMasker);
|
|
24461
24716
|
const resolved = await resolvePostmanApiKeyAndTeamId(
|
|
24462
24717
|
inputs,
|
package/dist/index.cjs
CHANGED
|
@@ -24622,20 +24622,22 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl) {
|
|
|
24622
24622
|
if (!payload) {
|
|
24623
24623
|
return void 0;
|
|
24624
24624
|
}
|
|
24625
|
-
const
|
|
24626
|
-
const
|
|
24625
|
+
const root = asRecord(payload.session) ?? payload;
|
|
24626
|
+
const identity = asRecord(root.identity);
|
|
24627
|
+
const data = asRecord(root.data);
|
|
24627
24628
|
const user = asRecord(data?.user);
|
|
24628
24629
|
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
24629
24630
|
const singleRole = coerceText(user?.role);
|
|
24630
24631
|
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
24631
24632
|
const resolved = {
|
|
24632
24633
|
source: "iapub/sessions",
|
|
24633
|
-
userId: coerceId(user?.id),
|
|
24634
|
+
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
24634
24635
|
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
24635
24636
|
teamId: coerceId(identity?.team),
|
|
24637
|
+
teamName: coerceText(user?.teamName),
|
|
24636
24638
|
teamDomain: coerceText(identity?.domain),
|
|
24637
24639
|
...roles ? { roles } : {},
|
|
24638
|
-
consumerType: coerceText(
|
|
24640
|
+
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
24639
24641
|
};
|
|
24640
24642
|
memoizedSessionIdentity = resolved;
|
|
24641
24643
|
return resolved;
|