azdo-cli 0.10.0-develop.394 → 0.10.0-develop.467
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
CHANGED
|
@@ -61,6 +61,13 @@ azdo pr comments --code-related-only # only file/line-anchored threads
|
|
|
61
61
|
azdo pr status # PR checks (status + branch policies) + code-comment counts
|
|
62
62
|
azdo pr comment-resolve 17 --pr-number 64 # idempotent: exit 0 even when already resolved
|
|
63
63
|
azdo pr comment-reopen 17 --pr-number 64
|
|
64
|
+
|
|
65
|
+
# Pipelines — list, inspect runs, wait (exit code = result), start
|
|
66
|
+
azdo pipeline list --filter ci
|
|
67
|
+
azdo pipeline get-runs 12 --branch develop --limit 1
|
|
68
|
+
azdo pipeline wait 3456 # blocks; exit 0 success / non-zero failure / 124 timeout
|
|
69
|
+
azdo pipeline get-run-detail 3456 # errors, failing tests, per-stage status
|
|
70
|
+
azdo pipeline start 12 --branch develop --parameter env=staging
|
|
64
71
|
```
|
|
65
72
|
|
|
66
73
|
## Documentation
|
|
@@ -65,6 +65,7 @@ var SETTINGS = [
|
|
|
65
65
|
}
|
|
66
66
|
];
|
|
67
67
|
var VALID_KEYS = SETTINGS.map((s) => s.key);
|
|
68
|
+
var SCOPED_KEYS = ["project", "fields", "markdown"];
|
|
68
69
|
function getConfigPath() {
|
|
69
70
|
return path.join(os.homedir(), ".azdo", "config.json");
|
|
70
71
|
}
|
|
@@ -88,6 +89,20 @@ function loadConfig() {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
function saveConfig(config) {
|
|
92
|
+
if (config.organizations) {
|
|
93
|
+
const normalised = {};
|
|
94
|
+
for (const [k, v] of Object.entries(config.organizations)) {
|
|
95
|
+
if (v && Object.keys(v).length > 0) {
|
|
96
|
+
normalised[k.toLowerCase()] = v;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (Object.keys(normalised).length > 0) {
|
|
100
|
+
config = { ...config, organizations: normalised };
|
|
101
|
+
} else {
|
|
102
|
+
const { organizations: _, ...rest } = config;
|
|
103
|
+
config = rest;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
91
106
|
const configPath = getConfigPath();
|
|
92
107
|
const dir = path.dirname(configPath);
|
|
93
108
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -126,6 +141,115 @@ function unsetConfigValue(key) {
|
|
|
126
141
|
delete config[key];
|
|
127
142
|
saveConfig(config);
|
|
128
143
|
}
|
|
144
|
+
function resolveScopedConfig(org) {
|
|
145
|
+
const config = loadConfig();
|
|
146
|
+
const base = {
|
|
147
|
+
project: config.project,
|
|
148
|
+
fields: config.fields,
|
|
149
|
+
markdown: config.markdown,
|
|
150
|
+
org: config.org
|
|
151
|
+
};
|
|
152
|
+
if (!org) return base;
|
|
153
|
+
const scope = config.organizations?.[org.toLowerCase()];
|
|
154
|
+
if (!scope) return base;
|
|
155
|
+
return {
|
|
156
|
+
org: config.org,
|
|
157
|
+
project: scope.project ?? config.project,
|
|
158
|
+
fields: scope.fields ?? config.fields,
|
|
159
|
+
markdown: scope.markdown ?? config.markdown
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function validateScopedKey(key) {
|
|
163
|
+
if (!SCOPED_KEYS.includes(key)) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Invalid scoped key "${key}". Valid scoped keys: ${SCOPED_KEYS.join(", ")}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function applyValueToScope(scope, key, value) {
|
|
170
|
+
if (key === "fields") {
|
|
171
|
+
return { ...scope, fields: value.split(",").map((s) => s.trim()).filter(Boolean) };
|
|
172
|
+
}
|
|
173
|
+
if (key === "markdown") {
|
|
174
|
+
if (value !== "true" && value !== "false") {
|
|
175
|
+
throw new Error(`Invalid value "${value}" for markdown. Must be "true" or "false".`);
|
|
176
|
+
}
|
|
177
|
+
return { ...scope, markdown: value === "true" };
|
|
178
|
+
}
|
|
179
|
+
return { ...scope, [key]: value };
|
|
180
|
+
}
|
|
181
|
+
function setOrgScopedValue(org, key, value) {
|
|
182
|
+
validateScopedKey(key);
|
|
183
|
+
const config = loadConfig();
|
|
184
|
+
const lc = org.toLowerCase();
|
|
185
|
+
const existing = config.organizations?.[lc] ?? {};
|
|
186
|
+
const updated = applyValueToScope(existing, key, value);
|
|
187
|
+
saveConfig({
|
|
188
|
+
...config,
|
|
189
|
+
organizations: { ...config.organizations, [lc]: updated }
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function unsetOrgScopedValue(org, key) {
|
|
193
|
+
validateScopedKey(key);
|
|
194
|
+
const config = loadConfig();
|
|
195
|
+
const lc = org.toLowerCase();
|
|
196
|
+
const scope = config.organizations?.[lc];
|
|
197
|
+
if (!scope) return;
|
|
198
|
+
const { [key]: _, ...rest } = scope;
|
|
199
|
+
saveConfig({
|
|
200
|
+
...config,
|
|
201
|
+
organizations: { ...config.organizations, [lc]: rest }
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function getOrgScopedValue(org, key) {
|
|
205
|
+
validateScopedKey(key);
|
|
206
|
+
const config = loadConfig();
|
|
207
|
+
const scope = config.organizations?.[org.toLowerCase()];
|
|
208
|
+
return scope?.[key];
|
|
209
|
+
}
|
|
210
|
+
function readScope(config, name) {
|
|
211
|
+
if (name === "default") {
|
|
212
|
+
const { org: _o, organizations: _orgs, ...defaults } = config;
|
|
213
|
+
return defaults;
|
|
214
|
+
}
|
|
215
|
+
return config.organizations?.[name.toLowerCase()] ?? {};
|
|
216
|
+
}
|
|
217
|
+
function copyOrgScope(from, to, force = false) {
|
|
218
|
+
const config = loadConfig();
|
|
219
|
+
const source = readScope(config, from);
|
|
220
|
+
const toLc = to.toLowerCase();
|
|
221
|
+
const dest = config.organizations?.[toLc] ?? {};
|
|
222
|
+
if (!force) {
|
|
223
|
+
const collisions = Object.keys(source).filter(
|
|
224
|
+
(k) => dest[k] !== void 0
|
|
225
|
+
);
|
|
226
|
+
if (collisions.length > 0) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Scope "${toLc}" already has values for: ${collisions.join(", ")}. Use --force to overwrite.`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
saveConfig({
|
|
233
|
+
...config,
|
|
234
|
+
organizations: {
|
|
235
|
+
...config.organizations,
|
|
236
|
+
[toLc]: { ...dest, ...source }
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function moveOrgScope(from, to, force = false) {
|
|
241
|
+
copyOrgScope(from, to, force);
|
|
242
|
+
if (from !== "default") {
|
|
243
|
+
deleteOrgScope(from);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function deleteOrgScope(name) {
|
|
247
|
+
const config = loadConfig();
|
|
248
|
+
const lc = name.toLowerCase();
|
|
249
|
+
if (!config.organizations?.[lc]) return;
|
|
250
|
+
const { [lc]: _, ...rest } = config.organizations;
|
|
251
|
+
saveConfig({ ...config, organizations: rest });
|
|
252
|
+
}
|
|
129
253
|
|
|
130
254
|
// src/services/oauth-config.ts
|
|
131
255
|
var DEFAULT_OAUTH_CLIENT_ID = "872cd9fa-d31f-45e0-9eab-6e460a02d1f1";
|
|
@@ -864,7 +988,7 @@ async function deletePat(org) {
|
|
|
864
988
|
}
|
|
865
989
|
try {
|
|
866
990
|
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
867
|
-
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-
|
|
991
|
+
const { lockPath: lockPath2 } = await import("./oauth-token-refresh-7FQ4VAIS.js");
|
|
868
992
|
unlinkSync2(lockPath2(org));
|
|
869
993
|
} catch {
|
|
870
994
|
}
|
|
@@ -1061,6 +1185,13 @@ export {
|
|
|
1061
1185
|
getConfigValue,
|
|
1062
1186
|
setConfigValue,
|
|
1063
1187
|
unsetConfigValue,
|
|
1188
|
+
resolveScopedConfig,
|
|
1189
|
+
setOrgScopedValue,
|
|
1190
|
+
unsetOrgScopedValue,
|
|
1191
|
+
getOrgScopedValue,
|
|
1192
|
+
copyOrgScope,
|
|
1193
|
+
moveOrgScope,
|
|
1194
|
+
deleteOrgScope,
|
|
1064
1195
|
maskedDisplay,
|
|
1065
1196
|
normalizePat,
|
|
1066
1197
|
AZDO_RESOURCE_ID,
|