azdo-cli 0.5.0-025-multi-org-support.449 → 0.5.0-025-multi-org-support.453
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.
|
@@ -186,7 +186,7 @@ function setOrgScopedValue(org, key, value) {
|
|
|
186
186
|
const updated = applyValueToScope(existing, key, value);
|
|
187
187
|
saveConfig({
|
|
188
188
|
...config,
|
|
189
|
-
organizations: { ...config.organizations
|
|
189
|
+
organizations: { ...config.organizations, [lc]: updated }
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
function unsetOrgScopedValue(org, key) {
|
|
@@ -198,7 +198,7 @@ function unsetOrgScopedValue(org, key) {
|
|
|
198
198
|
const { [key]: _, ...rest } = scope;
|
|
199
199
|
saveConfig({
|
|
200
200
|
...config,
|
|
201
|
-
organizations: { ...config.organizations
|
|
201
|
+
organizations: { ...config.organizations, [lc]: rest }
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
204
|
function getOrgScopedValue(org, key) {
|
|
@@ -232,7 +232,7 @@ function copyOrgScope(from, to, force = false) {
|
|
|
232
232
|
saveConfig({
|
|
233
233
|
...config,
|
|
234
234
|
organizations: {
|
|
235
|
-
...config.organizations
|
|
235
|
+
...config.organizations,
|
|
236
236
|
[toLc]: { ...dest, ...source }
|
|
237
237
|
}
|
|
238
238
|
});
|
|
@@ -988,7 +988,7 @@ async function deletePat(org) {
|
|
|
988
988
|
}
|
|
989
989
|
try {
|
|
990
990
|
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
991
|
-
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-
|
|
991
|
+
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-7FQ4VAIS.js");
|
|
992
992
|
unlinkSync2(lockPath2(org));
|
|
993
993
|
} catch {
|
|
994
994
|
}
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
tokenResponseToCredential,
|
|
36
36
|
unsetConfigValue,
|
|
37
37
|
unsetOrgScopedValue
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-XVXMDWQE.js";
|
|
39
39
|
|
|
40
40
|
// src/index.ts
|
|
41
41
|
import { Command as Command16 } from "commander";
|
|
@@ -264,49 +264,53 @@ async function getOrgFieldNames(context, cred) {
|
|
|
264
264
|
const data = await response.json();
|
|
265
265
|
return (data.value ?? []).map((f) => f.referenceName);
|
|
266
266
|
}
|
|
267
|
+
function buildCombinedDescription(fields) {
|
|
268
|
+
const parts = [];
|
|
269
|
+
if (fields["System.Description"]) {
|
|
270
|
+
parts.push({ label: "Description", value: fields["System.Description"] });
|
|
271
|
+
}
|
|
272
|
+
if (fields["Microsoft.VSTS.Common.AcceptanceCriteria"]) {
|
|
273
|
+
parts.push({ label: "Acceptance Criteria", value: fields["Microsoft.VSTS.Common.AcceptanceCriteria"] });
|
|
274
|
+
}
|
|
275
|
+
if (fields["Microsoft.VSTS.TCM.ReproSteps"]) {
|
|
276
|
+
parts.push({ label: "Repro Steps", value: fields["Microsoft.VSTS.TCM.ReproSteps"] });
|
|
277
|
+
}
|
|
278
|
+
if (parts.length === 0) return null;
|
|
279
|
+
if (parts.length === 1) return parts[0].value;
|
|
280
|
+
return parts.map((p) => `<h3>${p.label}</h3>${p.value}`).join("");
|
|
281
|
+
}
|
|
282
|
+
async function fetchWorkItemWithFallback(context, id, cred, normalizedExtraFields) {
|
|
283
|
+
try {
|
|
284
|
+
const data = await fetchWorkItemResponse(context, id, cred, {
|
|
285
|
+
fields: normalizeFieldList([...DEFAULT_FIELDS, ...normalizedExtraFields])
|
|
286
|
+
});
|
|
287
|
+
return { data, effectiveExtraFields: normalizedExtraFields };
|
|
288
|
+
} catch (err) {
|
|
289
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
290
|
+
if (!msg.includes("TF51535")) throw err;
|
|
291
|
+
const orgFields = new Set(await getOrgFieldNames(context, cred));
|
|
292
|
+
const missing = normalizedExtraFields.filter((f) => !orgFields.has(f));
|
|
293
|
+
const effectiveExtraFields = normalizedExtraFields.filter((f) => orgFields.has(f));
|
|
294
|
+
for (const f of missing) {
|
|
295
|
+
process.stderr.write(`Warning: field "${f}" does not exist in this org and will be skipped.
|
|
296
|
+
`);
|
|
297
|
+
}
|
|
298
|
+
const data = await fetchWorkItemResponse(context, id, cred, {
|
|
299
|
+
fields: normalizeFieldList([...DEFAULT_FIELDS, ...effectiveExtraFields])
|
|
300
|
+
});
|
|
301
|
+
return { data, effectiveExtraFields };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
267
304
|
async function getWorkItem(context, id, cred, extraFields) {
|
|
268
305
|
const normalizedExtraFields = extraFields ? normalizeFieldList(extraFields) : [];
|
|
269
306
|
let effectiveExtraFields = normalizedExtraFields;
|
|
270
307
|
let data;
|
|
271
308
|
if (normalizedExtraFields.length > 0) {
|
|
272
|
-
|
|
273
|
-
data = await fetchWorkItemResponse(context, id, cred, {
|
|
274
|
-
fields: normalizeFieldList([...DEFAULT_FIELDS, ...normalizedExtraFields])
|
|
275
|
-
});
|
|
276
|
-
} catch (err) {
|
|
277
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
278
|
-
if (!msg.includes("TF51535")) throw err;
|
|
279
|
-
const orgFields = new Set(await getOrgFieldNames(context, cred));
|
|
280
|
-
const missing = normalizedExtraFields.filter((f) => !orgFields.has(f));
|
|
281
|
-
effectiveExtraFields = normalizedExtraFields.filter((f) => orgFields.has(f));
|
|
282
|
-
for (const f of missing) {
|
|
283
|
-
process.stderr.write(`Warning: field "${f}" does not exist in this org and will be skipped.
|
|
284
|
-
`);
|
|
285
|
-
}
|
|
286
|
-
data = await fetchWorkItemResponse(context, id, cred, {
|
|
287
|
-
fields: normalizeFieldList([...DEFAULT_FIELDS, ...effectiveExtraFields])
|
|
288
|
-
});
|
|
289
|
-
}
|
|
309
|
+
({ data, effectiveExtraFields } = await fetchWorkItemWithFallback(context, id, cred, normalizedExtraFields));
|
|
290
310
|
} else {
|
|
291
311
|
data = await fetchWorkItemResponse(context, id, cred, { includeRelations: true });
|
|
292
312
|
}
|
|
293
313
|
const relationsData = normalizedExtraFields.length > 0 ? await fetchWorkItemResponse(context, id, cred, { includeRelations: true }) : data;
|
|
294
|
-
const descriptionParts = [];
|
|
295
|
-
if (data.fields["System.Description"]) {
|
|
296
|
-
descriptionParts.push({ label: "Description", value: data.fields["System.Description"] });
|
|
297
|
-
}
|
|
298
|
-
if (data.fields["Microsoft.VSTS.Common.AcceptanceCriteria"]) {
|
|
299
|
-
descriptionParts.push({ label: "Acceptance Criteria", value: data.fields["Microsoft.VSTS.Common.AcceptanceCriteria"] });
|
|
300
|
-
}
|
|
301
|
-
if (data.fields["Microsoft.VSTS.TCM.ReproSteps"]) {
|
|
302
|
-
descriptionParts.push({ label: "Repro Steps", value: data.fields["Microsoft.VSTS.TCM.ReproSteps"] });
|
|
303
|
-
}
|
|
304
|
-
let combinedDescription = null;
|
|
305
|
-
if (descriptionParts.length === 1) {
|
|
306
|
-
combinedDescription = descriptionParts.at(0)?.value ?? null;
|
|
307
|
-
} else if (descriptionParts.length > 1) {
|
|
308
|
-
combinedDescription = descriptionParts.map((p) => `<h3>${p.label}</h3>${p.value}`).join("");
|
|
309
|
-
}
|
|
310
314
|
return {
|
|
311
315
|
id: data.id,
|
|
312
316
|
rev: data.rev,
|
|
@@ -314,7 +318,7 @@ async function getWorkItem(context, id, cred, extraFields) {
|
|
|
314
318
|
state: data.fields["System.State"],
|
|
315
319
|
type: data.fields["System.WorkItemType"],
|
|
316
320
|
assignedTo: data.fields["System.AssignedTo"]?.displayName ?? null,
|
|
317
|
-
description:
|
|
321
|
+
description: buildCombinedDescription(data.fields),
|
|
318
322
|
areaPath: data.fields["System.AreaPath"],
|
|
319
323
|
iterationPath: data.fields["System.IterationPath"],
|
|
320
324
|
url: data._links.html.href,
|
|
@@ -840,33 +844,39 @@ var patterns = [
|
|
|
840
844
|
/^[^@]+@vs-ssh\.visualstudio\.com:v3\/([^/]+)\/([^/]+)\/([^/]+?)(?:\.git)?$/
|
|
841
845
|
];
|
|
842
846
|
var httpsEmbeddedSecret = /^https?:\/\/[^:@/]+:[^@/]+@/;
|
|
847
|
+
function parseSingleRemoteLine(line) {
|
|
848
|
+
const trimmed = line.trim();
|
|
849
|
+
if (!trimmed) return null;
|
|
850
|
+
const tabIdx = trimmed.indexOf(" ");
|
|
851
|
+
if (tabIdx === -1) return null;
|
|
852
|
+
const remoteName = trimmed.slice(0, tabIdx);
|
|
853
|
+
const afterTab = trimmed.slice(tabIdx + 1);
|
|
854
|
+
const urlEnd = afterTab.lastIndexOf(" (");
|
|
855
|
+
const url = urlEnd === -1 ? afterTab : afterTab.slice(0, urlEnd);
|
|
856
|
+
return { remoteName, url };
|
|
857
|
+
}
|
|
858
|
+
function matchAzdoRemote(remoteName, url) {
|
|
859
|
+
for (const pattern of patterns) {
|
|
860
|
+
const match = pattern.exec(url);
|
|
861
|
+
if (!match) continue;
|
|
862
|
+
const project = match[2];
|
|
863
|
+
if (/^DefaultCollection$/i.test(project)) return null;
|
|
864
|
+
return { remoteName, org: match[1], project, hasEmbeddedSecret: httpsEmbeddedSecret.test(url) };
|
|
865
|
+
}
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
843
868
|
function parseAllAzdoRemotes(output) {
|
|
844
869
|
const seen = /* @__PURE__ */ new Set();
|
|
845
870
|
const results = [];
|
|
846
871
|
for (const line of output.split("\n")) {
|
|
847
|
-
const
|
|
848
|
-
if (!
|
|
849
|
-
const
|
|
850
|
-
if (tabIdx === -1) continue;
|
|
851
|
-
const remoteName = trimmed.slice(0, tabIdx);
|
|
872
|
+
const parsed = parseSingleRemoteLine(line);
|
|
873
|
+
if (!parsed) continue;
|
|
874
|
+
const { remoteName, url } = parsed;
|
|
852
875
|
if (seen.has(remoteName)) continue;
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const match = pattern.exec(url);
|
|
858
|
-
if (match) {
|
|
859
|
-
const project = match[2];
|
|
860
|
-
if (/^DefaultCollection$/i.test(project)) break;
|
|
861
|
-
seen.add(remoteName);
|
|
862
|
-
results.push({
|
|
863
|
-
remoteName,
|
|
864
|
-
org: match[1],
|
|
865
|
-
project,
|
|
866
|
-
hasEmbeddedSecret: httpsEmbeddedSecret.test(url)
|
|
867
|
-
});
|
|
868
|
-
break;
|
|
869
|
-
}
|
|
876
|
+
const candidate = matchAzdoRemote(remoteName, url);
|
|
877
|
+
if (candidate) {
|
|
878
|
+
seen.add(remoteName);
|
|
879
|
+
results.push(candidate);
|
|
870
880
|
}
|
|
871
881
|
}
|
|
872
882
|
return results;
|
|
@@ -901,7 +911,7 @@ function readGitConfigContent() {
|
|
|
901
911
|
}
|
|
902
912
|
if (stat.isFile()) {
|
|
903
913
|
const ref = fs.readFileSync(gitPath, "utf-8");
|
|
904
|
-
const m = /^gitdir
|
|
914
|
+
const m = /^gitdir:[ \t]*([^\r\n]+)/m.exec(ref);
|
|
905
915
|
if (m) {
|
|
906
916
|
return fs.readFileSync(path.join(path.resolve(dir, m[1].trim()), "config"), "utf-8");
|
|
907
917
|
}
|
|
@@ -925,13 +935,13 @@ function gitConfigToRemoteLines(configContent) {
|
|
|
925
935
|
emittedUrl = false;
|
|
926
936
|
continue;
|
|
927
937
|
}
|
|
928
|
-
if (
|
|
938
|
+
if (line.startsWith("[")) {
|
|
929
939
|
currentRemote = null;
|
|
930
940
|
emittedUrl = false;
|
|
931
941
|
continue;
|
|
932
942
|
}
|
|
933
943
|
if (currentRemote && !emittedUrl) {
|
|
934
|
-
const urlMatch =
|
|
944
|
+
const urlMatch = /^[ \t]+url[ \t]*=[ \t]*([^\r\n]+)/.exec(line);
|
|
935
945
|
if (urlMatch) {
|
|
936
946
|
lines.push(`${currentRemote} ${urlMatch[1].trim()} (fetch)`);
|
|
937
947
|
emittedUrl = true;
|