@bragduck/cli 2.7.0 → 2.8.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/dist/bin/bragduck.js +513 -138
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js +277 -67
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/bragduck.js
CHANGED
|
@@ -22,7 +22,7 @@ var init_esm_shims = __esm({
|
|
|
22
22
|
import { config } from "dotenv";
|
|
23
23
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
24
24
|
import { dirname, join } from "path";
|
|
25
|
-
var __filename, __dirname, APP_NAME, CONFIG_KEYS, DEFAULT_CONFIG, OAUTH_CONFIG, API_ENDPOINTS, ENCRYPTION_CONFIG, STORAGE_PATHS, HTTP_STATUS;
|
|
25
|
+
var __filename, __dirname, APP_NAME, CONFIG_KEYS, DEFAULT_CONFIG, OAUTH_CONFIG, API_ENDPOINTS, ENCRYPTION_CONFIG, STORAGE_PATHS, HTTP_STATUS, CONFIG_FILES;
|
|
26
26
|
var init_constants = __esm({
|
|
27
27
|
"src/constants.ts"() {
|
|
28
28
|
"use strict";
|
|
@@ -33,11 +33,21 @@ var init_constants = __esm({
|
|
|
33
33
|
APP_NAME = "bragduck";
|
|
34
34
|
CONFIG_KEYS = {
|
|
35
35
|
DEFAULT_COMMIT_DAYS: "defaultCommitDays",
|
|
36
|
-
AUTO_VERSION_CHECK: "autoVersionCheck"
|
|
36
|
+
AUTO_VERSION_CHECK: "autoVersionCheck",
|
|
37
|
+
DEFAULT_SOURCE: "defaultSource",
|
|
38
|
+
SOURCE_PRIORITY: "sourcePriority",
|
|
39
|
+
JIRA_INSTANCE: "jiraInstance",
|
|
40
|
+
CONFLUENCE_INSTANCE: "confluenceInstance",
|
|
41
|
+
GITLAB_INSTANCE: "gitlabInstance"
|
|
37
42
|
};
|
|
38
43
|
DEFAULT_CONFIG = {
|
|
39
44
|
defaultCommitDays: 30,
|
|
40
|
-
autoVersionCheck: true
|
|
45
|
+
autoVersionCheck: true,
|
|
46
|
+
defaultSource: void 0,
|
|
47
|
+
sourcePriority: void 0,
|
|
48
|
+
jiraInstance: void 0,
|
|
49
|
+
confluenceInstance: void 0,
|
|
50
|
+
gitlabInstance: void 0
|
|
41
51
|
};
|
|
42
52
|
OAUTH_CONFIG = {
|
|
43
53
|
CLIENT_ID: "bragduck-cli",
|
|
@@ -92,6 +102,258 @@ var init_constants = __esm({
|
|
|
92
102
|
NOT_FOUND: 404,
|
|
93
103
|
INTERNAL_SERVER_ERROR: 500
|
|
94
104
|
};
|
|
105
|
+
CONFIG_FILES = [".bragduckrc", ".bragduck/config.json", ".bragduckrc.json"];
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// src/utils/env-loader.ts
|
|
110
|
+
function parseEnvNumber(key) {
|
|
111
|
+
const value = process.env[key];
|
|
112
|
+
if (!value) return void 0;
|
|
113
|
+
const parsed = parseInt(value, 10);
|
|
114
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
115
|
+
}
|
|
116
|
+
function parseEnvBoolean(key) {
|
|
117
|
+
const value = process.env[key]?.toLowerCase();
|
|
118
|
+
if (!value) return void 0;
|
|
119
|
+
if (["true", "yes", "1"].includes(value)) return true;
|
|
120
|
+
if (["false", "no", "0"].includes(value)) return false;
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
function parseEnvArray(key) {
|
|
124
|
+
const value = process.env[key];
|
|
125
|
+
if (!value) return void 0;
|
|
126
|
+
const items = value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
127
|
+
return items.length > 0 ? items : void 0;
|
|
128
|
+
}
|
|
129
|
+
function parseEnvSource(key) {
|
|
130
|
+
const value = process.env[key];
|
|
131
|
+
if (!value) return void 0;
|
|
132
|
+
const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
|
|
133
|
+
return validSources.includes(value) ? value : void 0;
|
|
134
|
+
}
|
|
135
|
+
function loadEnvConfig() {
|
|
136
|
+
return {
|
|
137
|
+
// Source configuration
|
|
138
|
+
source: parseEnvSource("BRAGDUCK_SOURCE"),
|
|
139
|
+
jiraInstance: process.env.BRAGDUCK_JIRA_INSTANCE,
|
|
140
|
+
confluenceInstance: process.env.BRAGDUCK_CONFLUENCE_INSTANCE,
|
|
141
|
+
gitlabInstance: process.env.BRAGDUCK_GITLAB_INSTANCE,
|
|
142
|
+
// Behavior configuration
|
|
143
|
+
defaultCommitDays: parseEnvNumber("BRAGDUCK_DEFAULT_COMMIT_DAYS"),
|
|
144
|
+
autoVersionCheck: parseEnvBoolean("BRAGDUCK_AUTO_VERSION_CHECK"),
|
|
145
|
+
sourcePriority: parseEnvArray("BRAGDUCK_SOURCE_PRIORITY"),
|
|
146
|
+
// Credential overrides (for CI/CD)
|
|
147
|
+
jiraToken: process.env.BRAGDUCK_JIRA_TOKEN,
|
|
148
|
+
confluenceToken: process.env.BRAGDUCK_CONFLUENCE_TOKEN,
|
|
149
|
+
gitlabToken: process.env.BRAGDUCK_GITLAB_TOKEN
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
var init_env_loader = __esm({
|
|
153
|
+
"src/utils/env-loader.ts"() {
|
|
154
|
+
"use strict";
|
|
155
|
+
init_esm_shims();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// src/utils/errors.ts
|
|
160
|
+
var BragduckError, AuthenticationError, GitError, ApiError, NetworkError, ValidationError, OAuthError, TokenExpiredError, GitHubError, BitbucketError, GitLabError, JiraError, ConfluenceError;
|
|
161
|
+
var init_errors = __esm({
|
|
162
|
+
"src/utils/errors.ts"() {
|
|
163
|
+
"use strict";
|
|
164
|
+
init_esm_shims();
|
|
165
|
+
BragduckError = class extends Error {
|
|
166
|
+
code;
|
|
167
|
+
details;
|
|
168
|
+
constructor(message, code, details) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.name = "BragduckError";
|
|
171
|
+
this.code = code;
|
|
172
|
+
this.details = details;
|
|
173
|
+
Error.captureStackTrace(this, this.constructor);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
AuthenticationError = class extends BragduckError {
|
|
177
|
+
constructor(message, details) {
|
|
178
|
+
super(message, "AUTH_ERROR", details);
|
|
179
|
+
this.name = "AuthenticationError";
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
GitError = class extends BragduckError {
|
|
183
|
+
constructor(message, details) {
|
|
184
|
+
super(message, "GIT_ERROR", details);
|
|
185
|
+
this.name = "GitError";
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
ApiError = class extends BragduckError {
|
|
189
|
+
statusCode;
|
|
190
|
+
constructor(message, statusCode, details) {
|
|
191
|
+
super(message, "API_ERROR", details);
|
|
192
|
+
this.name = "ApiError";
|
|
193
|
+
this.statusCode = statusCode;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
NetworkError = class extends BragduckError {
|
|
197
|
+
constructor(message, details) {
|
|
198
|
+
super(message, "NETWORK_ERROR", details);
|
|
199
|
+
this.name = "NetworkError";
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
ValidationError = class extends BragduckError {
|
|
203
|
+
constructor(message, details) {
|
|
204
|
+
super(message, "VALIDATION_ERROR", details);
|
|
205
|
+
this.name = "ValidationError";
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
OAuthError = class extends AuthenticationError {
|
|
209
|
+
constructor(message, details) {
|
|
210
|
+
super(message, details);
|
|
211
|
+
this.name = "OAuthError";
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
TokenExpiredError = class extends AuthenticationError {
|
|
215
|
+
constructor(message = "Authentication token has expired") {
|
|
216
|
+
super(message);
|
|
217
|
+
this.name = "TokenExpiredError";
|
|
218
|
+
this.code = "TOKEN_EXPIRED";
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
GitHubError = class extends BragduckError {
|
|
222
|
+
constructor(message, details) {
|
|
223
|
+
super(message, "GITHUB_ERROR", details);
|
|
224
|
+
this.name = "GitHubError";
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
BitbucketError = class extends BragduckError {
|
|
228
|
+
constructor(message, details) {
|
|
229
|
+
super(message, "BITBUCKET_ERROR", details);
|
|
230
|
+
this.name = "BitbucketError";
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
GitLabError = class extends BragduckError {
|
|
234
|
+
constructor(message, details) {
|
|
235
|
+
super(message, "GITLAB_ERROR", details);
|
|
236
|
+
this.name = "GitLabError";
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
JiraError = class extends BragduckError {
|
|
240
|
+
constructor(message, details) {
|
|
241
|
+
super(message, "JIRA_ERROR", details);
|
|
242
|
+
this.name = "JiraError";
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
ConfluenceError = class extends BragduckError {
|
|
246
|
+
constructor(message, details) {
|
|
247
|
+
super(message, "CONFLUENCE_ERROR", details);
|
|
248
|
+
this.name = "ConfluenceError";
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// src/utils/config-loader.ts
|
|
255
|
+
import { promises as fs } from "fs";
|
|
256
|
+
import path2 from "path";
|
|
257
|
+
async function findProjectConfig() {
|
|
258
|
+
const cwd = process.cwd();
|
|
259
|
+
if (configCache.has(cwd)) {
|
|
260
|
+
return configCache.get(cwd) || null;
|
|
261
|
+
}
|
|
262
|
+
for (const filename of CONFIG_FILES) {
|
|
263
|
+
const configPath = path2.join(cwd, filename);
|
|
264
|
+
try {
|
|
265
|
+
await fs.access(configPath);
|
|
266
|
+
const config2 = await loadAndValidateConfig(configPath);
|
|
267
|
+
configCache.set(cwd, config2);
|
|
268
|
+
return config2;
|
|
269
|
+
} catch {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
configCache.set(cwd, null);
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
async function loadAndValidateConfig(configPath) {
|
|
277
|
+
try {
|
|
278
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
279
|
+
const config2 = JSON.parse(content);
|
|
280
|
+
validateProjectConfig(config2);
|
|
281
|
+
return config2;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error instanceof SyntaxError) {
|
|
284
|
+
throw new ValidationError(`Invalid JSON in config file: ${configPath}`, {
|
|
285
|
+
originalError: error.message
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function validateProjectConfig(config2) {
|
|
292
|
+
if (typeof config2 !== "object" || config2 === null) {
|
|
293
|
+
throw new ValidationError("Config must be a JSON object");
|
|
294
|
+
}
|
|
295
|
+
const cfg = config2;
|
|
296
|
+
if (cfg.defaultCommitDays !== void 0) {
|
|
297
|
+
if (typeof cfg.defaultCommitDays !== "number") {
|
|
298
|
+
throw new ValidationError("defaultCommitDays must be a number");
|
|
299
|
+
}
|
|
300
|
+
if (cfg.defaultCommitDays < 1 || cfg.defaultCommitDays > 365) {
|
|
301
|
+
throw new ValidationError("defaultCommitDays must be between 1 and 365");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (cfg.autoVersionCheck !== void 0) {
|
|
305
|
+
if (typeof cfg.autoVersionCheck !== "boolean") {
|
|
306
|
+
throw new ValidationError("autoVersionCheck must be a boolean");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (cfg.defaultSource !== void 0) {
|
|
310
|
+
const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
|
|
311
|
+
if (!validSources.includes(cfg.defaultSource)) {
|
|
312
|
+
throw new ValidationError(
|
|
313
|
+
`Invalid defaultSource: ${cfg.defaultSource}. Must be one of: ${validSources.join(", ")}`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (cfg.sourcePriority !== void 0) {
|
|
318
|
+
if (!Array.isArray(cfg.sourcePriority)) {
|
|
319
|
+
throw new ValidationError("sourcePriority must be an array");
|
|
320
|
+
}
|
|
321
|
+
const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
|
|
322
|
+
for (const source of cfg.sourcePriority) {
|
|
323
|
+
if (!validSources.includes(source)) {
|
|
324
|
+
throw new ValidationError(
|
|
325
|
+
`Invalid source in sourcePriority: ${source}. Must be one of: ${validSources.join(", ")}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const instanceFields = ["jiraInstance", "confluenceInstance", "gitlabInstance"];
|
|
331
|
+
for (const field of instanceFields) {
|
|
332
|
+
if (cfg[field] !== void 0) {
|
|
333
|
+
if (typeof cfg[field] !== "string") {
|
|
334
|
+
throw new ValidationError(`${field} must be a string`);
|
|
335
|
+
}
|
|
336
|
+
const value = cfg[field];
|
|
337
|
+
if (!value.match(/^[a-zA-Z0-9.-]+(:[0-9]+)?(\/.*)?$/)) {
|
|
338
|
+
throw new ValidationError(`Invalid ${field}: ${value}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const booleanFields = ["requireAuthentication", "skipSourceDetection"];
|
|
343
|
+
for (const field of booleanFields) {
|
|
344
|
+
if (cfg[field] !== void 0 && typeof cfg[field] !== "boolean") {
|
|
345
|
+
throw new ValidationError(`${field} must be a boolean`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
var configCache;
|
|
350
|
+
var init_config_loader = __esm({
|
|
351
|
+
"src/utils/config-loader.ts"() {
|
|
352
|
+
"use strict";
|
|
353
|
+
init_esm_shims();
|
|
354
|
+
init_constants();
|
|
355
|
+
init_errors();
|
|
356
|
+
configCache = /* @__PURE__ */ new Map();
|
|
95
357
|
}
|
|
96
358
|
});
|
|
97
359
|
|
|
@@ -107,6 +369,8 @@ var init_storage_service = __esm({
|
|
|
107
369
|
"use strict";
|
|
108
370
|
init_esm_shims();
|
|
109
371
|
init_constants();
|
|
372
|
+
init_env_loader();
|
|
373
|
+
init_config_loader();
|
|
110
374
|
StorageService = class {
|
|
111
375
|
config;
|
|
112
376
|
storageBackend;
|
|
@@ -220,17 +484,10 @@ var init_storage_service = __esm({
|
|
|
220
484
|
}
|
|
221
485
|
}
|
|
222
486
|
/**
|
|
223
|
-
* Check if user is authenticated
|
|
487
|
+
* Check if user is authenticated (checks bragduck service)
|
|
224
488
|
*/
|
|
225
489
|
async isAuthenticated() {
|
|
226
|
-
|
|
227
|
-
if (!credentials || !credentials.accessToken) {
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
if (credentials.expiresAt && credentials.expiresAt < Date.now()) {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
return true;
|
|
490
|
+
return this.isServiceAuthenticated("bragduck");
|
|
234
491
|
}
|
|
235
492
|
/**
|
|
236
493
|
* Get credentials for a specific service
|
|
@@ -345,7 +602,27 @@ var init_storage_service = __esm({
|
|
|
345
602
|
this.config.delete("oauthState");
|
|
346
603
|
}
|
|
347
604
|
/**
|
|
348
|
-
* Get configuration value
|
|
605
|
+
* Get configuration value with hierarchy resolution
|
|
606
|
+
* Priority: env vars > project config > user config > defaults
|
|
607
|
+
*/
|
|
608
|
+
async getConfigWithHierarchy(key) {
|
|
609
|
+
const envConfig = loadEnvConfig();
|
|
610
|
+
const envKey = key === "defaultSource" ? "source" : key;
|
|
611
|
+
if (envConfig[envKey] !== void 0) {
|
|
612
|
+
return envConfig[envKey];
|
|
613
|
+
}
|
|
614
|
+
const projectConfig = await findProjectConfig();
|
|
615
|
+
if (projectConfig && projectConfig[key] !== void 0) {
|
|
616
|
+
return projectConfig[key];
|
|
617
|
+
}
|
|
618
|
+
const userValue = this.config.get(key);
|
|
619
|
+
if (userValue !== void 0) {
|
|
620
|
+
return userValue;
|
|
621
|
+
}
|
|
622
|
+
return DEFAULT_CONFIG[key];
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Get configuration value (uses hierarchy resolution)
|
|
349
626
|
*/
|
|
350
627
|
getConfig(key) {
|
|
351
628
|
return this.config.get(key);
|
|
@@ -357,7 +634,32 @@ var init_storage_service = __esm({
|
|
|
357
634
|
this.config.set(key, value);
|
|
358
635
|
}
|
|
359
636
|
/**
|
|
360
|
-
* Get all configuration
|
|
637
|
+
* Get all configuration (merges all layers)
|
|
638
|
+
* Priority: env vars > project config > user config > defaults
|
|
639
|
+
*/
|
|
640
|
+
async getAllConfigWithHierarchy() {
|
|
641
|
+
const envConfig = loadEnvConfig();
|
|
642
|
+
const projectConfig = await findProjectConfig();
|
|
643
|
+
const userConfig = this.config.store;
|
|
644
|
+
return {
|
|
645
|
+
...DEFAULT_CONFIG,
|
|
646
|
+
...userConfig,
|
|
647
|
+
...projectConfig,
|
|
648
|
+
...envConfig.source && { defaultSource: envConfig.source },
|
|
649
|
+
...envConfig.jiraInstance && { jiraInstance: envConfig.jiraInstance },
|
|
650
|
+
...envConfig.confluenceInstance && { confluenceInstance: envConfig.confluenceInstance },
|
|
651
|
+
...envConfig.gitlabInstance && { gitlabInstance: envConfig.gitlabInstance },
|
|
652
|
+
...envConfig.defaultCommitDays !== void 0 && {
|
|
653
|
+
defaultCommitDays: envConfig.defaultCommitDays
|
|
654
|
+
},
|
|
655
|
+
...envConfig.autoVersionCheck !== void 0 && {
|
|
656
|
+
autoVersionCheck: envConfig.autoVersionCheck
|
|
657
|
+
},
|
|
658
|
+
...envConfig.sourcePriority && { sourcePriority: envConfig.sourcePriority }
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Get all configuration (user config only)
|
|
361
663
|
*/
|
|
362
664
|
getAllConfig() {
|
|
363
665
|
return this.config.store;
|
|
@@ -368,7 +670,9 @@ var init_storage_service = __esm({
|
|
|
368
670
|
resetConfig() {
|
|
369
671
|
this.config.clear();
|
|
370
672
|
Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
|
|
371
|
-
|
|
673
|
+
if (value !== void 0) {
|
|
674
|
+
this.config.set(key, value);
|
|
675
|
+
}
|
|
372
676
|
});
|
|
373
677
|
}
|
|
374
678
|
/**
|
|
@@ -391,101 +695,6 @@ var init_storage_service = __esm({
|
|
|
391
695
|
}
|
|
392
696
|
});
|
|
393
697
|
|
|
394
|
-
// src/utils/errors.ts
|
|
395
|
-
var BragduckError, AuthenticationError, GitError, ApiError, NetworkError, ValidationError, OAuthError, TokenExpiredError, GitHubError, BitbucketError, GitLabError, JiraError, ConfluenceError;
|
|
396
|
-
var init_errors = __esm({
|
|
397
|
-
"src/utils/errors.ts"() {
|
|
398
|
-
"use strict";
|
|
399
|
-
init_esm_shims();
|
|
400
|
-
BragduckError = class extends Error {
|
|
401
|
-
code;
|
|
402
|
-
details;
|
|
403
|
-
constructor(message, code, details) {
|
|
404
|
-
super(message);
|
|
405
|
-
this.name = "BragduckError";
|
|
406
|
-
this.code = code;
|
|
407
|
-
this.details = details;
|
|
408
|
-
Error.captureStackTrace(this, this.constructor);
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
AuthenticationError = class extends BragduckError {
|
|
412
|
-
constructor(message, details) {
|
|
413
|
-
super(message, "AUTH_ERROR", details);
|
|
414
|
-
this.name = "AuthenticationError";
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
GitError = class extends BragduckError {
|
|
418
|
-
constructor(message, details) {
|
|
419
|
-
super(message, "GIT_ERROR", details);
|
|
420
|
-
this.name = "GitError";
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
ApiError = class extends BragduckError {
|
|
424
|
-
statusCode;
|
|
425
|
-
constructor(message, statusCode, details) {
|
|
426
|
-
super(message, "API_ERROR", details);
|
|
427
|
-
this.name = "ApiError";
|
|
428
|
-
this.statusCode = statusCode;
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
NetworkError = class extends BragduckError {
|
|
432
|
-
constructor(message, details) {
|
|
433
|
-
super(message, "NETWORK_ERROR", details);
|
|
434
|
-
this.name = "NetworkError";
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
ValidationError = class extends BragduckError {
|
|
438
|
-
constructor(message, details) {
|
|
439
|
-
super(message, "VALIDATION_ERROR", details);
|
|
440
|
-
this.name = "ValidationError";
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
OAuthError = class extends AuthenticationError {
|
|
444
|
-
constructor(message, details) {
|
|
445
|
-
super(message, details);
|
|
446
|
-
this.name = "OAuthError";
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
TokenExpiredError = class extends AuthenticationError {
|
|
450
|
-
constructor(message = "Authentication token has expired") {
|
|
451
|
-
super(message);
|
|
452
|
-
this.name = "TokenExpiredError";
|
|
453
|
-
this.code = "TOKEN_EXPIRED";
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
GitHubError = class extends BragduckError {
|
|
457
|
-
constructor(message, details) {
|
|
458
|
-
super(message, "GITHUB_ERROR", details);
|
|
459
|
-
this.name = "GitHubError";
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
BitbucketError = class extends BragduckError {
|
|
463
|
-
constructor(message, details) {
|
|
464
|
-
super(message, "BITBUCKET_ERROR", details);
|
|
465
|
-
this.name = "BitbucketError";
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
GitLabError = class extends BragduckError {
|
|
469
|
-
constructor(message, details) {
|
|
470
|
-
super(message, "GITLAB_ERROR", details);
|
|
471
|
-
this.name = "GitLabError";
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
JiraError = class extends BragduckError {
|
|
475
|
-
constructor(message, details) {
|
|
476
|
-
super(message, "JIRA_ERROR", details);
|
|
477
|
-
this.name = "JiraError";
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
ConfluenceError = class extends BragduckError {
|
|
481
|
-
constructor(message, details) {
|
|
482
|
-
super(message, "CONFLUENCE_ERROR", details);
|
|
483
|
-
this.name = "ConfluenceError";
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
|
|
489
698
|
// src/utils/logger.ts
|
|
490
699
|
import chalk from "chalk";
|
|
491
700
|
var logger;
|
|
@@ -1976,12 +2185,13 @@ init_errors();
|
|
|
1976
2185
|
init_storage_service();
|
|
1977
2186
|
import { exec as exec2 } from "child_process";
|
|
1978
2187
|
import { promisify as promisify2 } from "util";
|
|
2188
|
+
import { select } from "@inquirer/prompts";
|
|
1979
2189
|
var execAsync2 = promisify2(exec2);
|
|
1980
2190
|
var SourceDetector = class {
|
|
1981
2191
|
/**
|
|
1982
2192
|
* Detect all possible sources from git remotes
|
|
1983
2193
|
*/
|
|
1984
|
-
async detectSources() {
|
|
2194
|
+
async detectSources(options = {}) {
|
|
1985
2195
|
const detected = [];
|
|
1986
2196
|
try {
|
|
1987
2197
|
const { stdout } = await execAsync2("git remote -v");
|
|
@@ -2000,9 +2210,53 @@ var SourceDetector = class {
|
|
|
2000
2210
|
} catch {
|
|
2001
2211
|
throw new GitError("Not a git repository");
|
|
2002
2212
|
}
|
|
2003
|
-
|
|
2213
|
+
let recommended;
|
|
2214
|
+
if (detected.length > 1 && options.allowInteractive && process.stdout.isTTY) {
|
|
2215
|
+
try {
|
|
2216
|
+
recommended = await this.promptSourceSelection(detected, options.showAuthStatus);
|
|
2217
|
+
} catch {
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
if (!recommended && options.respectPriority) {
|
|
2221
|
+
const priority = await storageService.getConfigWithHierarchy("sourcePriority");
|
|
2222
|
+
if (priority) {
|
|
2223
|
+
recommended = this.applyPriority(detected, priority);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
if (!recommended) {
|
|
2227
|
+
recommended = this.selectRecommendedSource(detected);
|
|
2228
|
+
}
|
|
2004
2229
|
return { detected, recommended };
|
|
2005
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Prompt user to select a source interactively
|
|
2233
|
+
*/
|
|
2234
|
+
async promptSourceSelection(sources, showAuthStatus = true) {
|
|
2235
|
+
const choices = sources.map((source) => {
|
|
2236
|
+
const authStatus2 = showAuthStatus ? source.isAuthenticated ? "\u2713 authenticated" : "\u2717 not authenticated" : "";
|
|
2237
|
+
const repo = source.owner && source.repo ? `${source.owner}/${source.repo}` : source.host;
|
|
2238
|
+
const name = `${source.type}${authStatus2 ? ` (${authStatus2})` : ""} - ${repo}`;
|
|
2239
|
+
return {
|
|
2240
|
+
name,
|
|
2241
|
+
value: source.type,
|
|
2242
|
+
description: source.remoteUrl
|
|
2243
|
+
};
|
|
2244
|
+
});
|
|
2245
|
+
return await select({
|
|
2246
|
+
message: "Multiple sources detected. Which do you want to sync?",
|
|
2247
|
+
choices
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Apply configured priority to select source
|
|
2252
|
+
*/
|
|
2253
|
+
applyPriority(sources, priority) {
|
|
2254
|
+
for (const sourceType of priority) {
|
|
2255
|
+
const found = sources.find((s) => s.type === sourceType);
|
|
2256
|
+
if (found) return found.type;
|
|
2257
|
+
}
|
|
2258
|
+
return void 0;
|
|
2259
|
+
}
|
|
2006
2260
|
/**
|
|
2007
2261
|
* Parse git remote -v output
|
|
2008
2262
|
*/
|
|
@@ -2102,6 +2356,10 @@ var SourceDetector = class {
|
|
|
2102
2356
|
};
|
|
2103
2357
|
var sourceDetector = new SourceDetector();
|
|
2104
2358
|
|
|
2359
|
+
// src/commands/sync.ts
|
|
2360
|
+
init_env_loader();
|
|
2361
|
+
init_config_loader();
|
|
2362
|
+
|
|
2105
2363
|
// src/sync/adapter-factory.ts
|
|
2106
2364
|
init_esm_shims();
|
|
2107
2365
|
|
|
@@ -2124,13 +2382,13 @@ init_esm_shims();
|
|
|
2124
2382
|
init_errors();
|
|
2125
2383
|
import { existsSync as existsSync2 } from "fs";
|
|
2126
2384
|
import { join as join6 } from "path";
|
|
2127
|
-
function validateGitRepository(
|
|
2128
|
-
const gitDir = join6(
|
|
2385
|
+
function validateGitRepository(path3) {
|
|
2386
|
+
const gitDir = join6(path3, ".git");
|
|
2129
2387
|
if (!existsSync2(gitDir)) {
|
|
2130
2388
|
throw new GitError(
|
|
2131
2389
|
"Not a git repository. Please run this command from within a git repository.",
|
|
2132
2390
|
{
|
|
2133
|
-
path:
|
|
2391
|
+
path: path3,
|
|
2134
2392
|
hint: 'Run "git init" to initialize a git repository, or navigate to an existing one'
|
|
2135
2393
|
}
|
|
2136
2394
|
);
|
|
@@ -3851,7 +4109,7 @@ init_logger();
|
|
|
3851
4109
|
|
|
3852
4110
|
// src/ui/prompts.ts
|
|
3853
4111
|
init_esm_shims();
|
|
3854
|
-
import { checkbox, confirm, input as input2, select, editor } from "@inquirer/prompts";
|
|
4112
|
+
import { checkbox, confirm, input as input2, select as select2, editor } from "@inquirer/prompts";
|
|
3855
4113
|
import boxen4 from "boxen";
|
|
3856
4114
|
|
|
3857
4115
|
// src/ui/formatters.ts
|
|
@@ -4007,7 +4265,7 @@ async function promptDaysToScan(defaultDays = 30) {
|
|
|
4007
4265
|
{ name: "90 days", value: "90", description: "Last 3 months" },
|
|
4008
4266
|
{ name: "Custom", value: "custom", description: "Enter custom number of days" }
|
|
4009
4267
|
];
|
|
4010
|
-
const selected = await
|
|
4268
|
+
const selected = await select2({
|
|
4011
4269
|
message: "How many days back should we scan for PRs?",
|
|
4012
4270
|
choices,
|
|
4013
4271
|
default: "30"
|
|
@@ -4039,7 +4297,7 @@ async function promptSortOption() {
|
|
|
4039
4297
|
{ name: "By files (most files)", value: "files", description: "Most files changed" },
|
|
4040
4298
|
{ name: "No sorting", value: "none", description: "Keep original order" }
|
|
4041
4299
|
];
|
|
4042
|
-
return await
|
|
4300
|
+
return await select2({
|
|
4043
4301
|
message: "How would you like to sort the PRs?",
|
|
4044
4302
|
choices,
|
|
4045
4303
|
default: "date"
|
|
@@ -4053,7 +4311,7 @@ async function promptSelectOrganisation(organisations) {
|
|
|
4053
4311
|
value: org.id
|
|
4054
4312
|
}))
|
|
4055
4313
|
];
|
|
4056
|
-
const selected = await
|
|
4314
|
+
const selected = await select2({
|
|
4057
4315
|
message: "Attach brags to which company?",
|
|
4058
4316
|
choices,
|
|
4059
4317
|
default: "none"
|
|
@@ -4103,7 +4361,7 @@ ${theme.label("PR Link")} ${colors.link(prUrl)}`;
|
|
|
4103
4361
|
}
|
|
4104
4362
|
console.log(boxen4(bragDetails, boxStyles.info));
|
|
4105
4363
|
console.log("");
|
|
4106
|
-
const action = await
|
|
4364
|
+
const action = await select2({
|
|
4107
4365
|
message: `What would you like to do with this brag?`,
|
|
4108
4366
|
choices: [
|
|
4109
4367
|
{ name: "\u2713 Accept", value: "accept", description: "Add this brag as-is" },
|
|
@@ -4201,6 +4459,9 @@ async function ensureAuthenticated() {
|
|
|
4201
4459
|
}
|
|
4202
4460
|
}
|
|
4203
4461
|
|
|
4462
|
+
// src/commands/sync.ts
|
|
4463
|
+
init_errors();
|
|
4464
|
+
|
|
4204
4465
|
// src/ui/spinners.ts
|
|
4205
4466
|
init_esm_shims();
|
|
4206
4467
|
import ora2 from "ora";
|
|
@@ -4277,42 +4538,89 @@ async function syncCommand(options = {}) {
|
|
|
4277
4538
|
const detectionSpinner = createStepSpinner(1, TOTAL_STEPS, "Detecting repository source");
|
|
4278
4539
|
detectionSpinner.start();
|
|
4279
4540
|
let sourceType = options.source;
|
|
4541
|
+
if (!sourceType) {
|
|
4542
|
+
const envConfig = loadEnvConfig();
|
|
4543
|
+
sourceType = envConfig.source;
|
|
4544
|
+
}
|
|
4545
|
+
if (!sourceType) {
|
|
4546
|
+
const projectConfig = await findProjectConfig();
|
|
4547
|
+
sourceType = projectConfig?.defaultSource;
|
|
4548
|
+
}
|
|
4280
4549
|
if (!sourceType) {
|
|
4281
4550
|
try {
|
|
4282
|
-
const detectionResult = await sourceDetector.detectSources(
|
|
4551
|
+
const detectionResult = await sourceDetector.detectSources({
|
|
4552
|
+
allowInteractive: true,
|
|
4553
|
+
respectPriority: true,
|
|
4554
|
+
showAuthStatus: true
|
|
4555
|
+
});
|
|
4283
4556
|
sourceType = detectionResult.recommended;
|
|
4557
|
+
if (detectionResult.detected.length > 1) {
|
|
4558
|
+
logger.debug(
|
|
4559
|
+
`Detected sources: ${detectionResult.detected.map((s) => s.type).join(", ")}`
|
|
4560
|
+
);
|
|
4561
|
+
}
|
|
4284
4562
|
if (!sourceType && detectionResult.detected.length === 0) {
|
|
4285
4563
|
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, "No supported sources detected");
|
|
4286
4564
|
logger.log("");
|
|
4287
4565
|
logger.info("Make sure you are in a git repository with a remote URL");
|
|
4288
|
-
logger.info("Or use --source flag for non-git sources:
|
|
4566
|
+
logger.info("Or use --source flag for non-git sources:");
|
|
4567
|
+
logger.info(` ${theme.command("bragduck sync --source jira")}`);
|
|
4568
|
+
logger.info(` ${theme.command("bragduck sync --source confluence")}`);
|
|
4289
4569
|
return;
|
|
4290
4570
|
}
|
|
4291
|
-
} catch {
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4571
|
+
} catch (error) {
|
|
4572
|
+
if (error instanceof GitError) {
|
|
4573
|
+
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, "Not a git repository");
|
|
4574
|
+
logger.log("");
|
|
4575
|
+
logger.info("For non-git sources, use --source flag:");
|
|
4576
|
+
logger.info(` ${theme.command("bragduck sync --source jira")}`);
|
|
4577
|
+
logger.info(` ${theme.command("bragduck sync --source confluence")}`);
|
|
4578
|
+
logger.log("");
|
|
4579
|
+
logger.info("Or set default source in config:");
|
|
4580
|
+
logger.info(` ${theme.command("bragduck config set defaultSource jira")}`);
|
|
4581
|
+
return;
|
|
4582
|
+
}
|
|
4583
|
+
throw error;
|
|
4296
4584
|
}
|
|
4297
4585
|
}
|
|
4298
|
-
if (!sourceType) {
|
|
4586
|
+
if (!sourceType || !AdapterFactory.isSupported(sourceType)) {
|
|
4299
4587
|
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, "Could not determine source");
|
|
4588
|
+
try {
|
|
4589
|
+
const detected = await sourceDetector.detectSources();
|
|
4590
|
+
if (detected.detected.length > 0) {
|
|
4591
|
+
logger.log("");
|
|
4592
|
+
logger.info("Detected sources:");
|
|
4593
|
+
for (const source of detected.detected) {
|
|
4594
|
+
const authStatus2 = source.isAuthenticated ? "\u2713 authenticated" : "\u2717 not authenticated";
|
|
4595
|
+
const repo = source.owner && source.repo ? `${source.owner}/${source.repo}` : source.host || "configured";
|
|
4596
|
+
logger.info(` \u2022 ${source.type} (${authStatus2}) - ${repo}`);
|
|
4597
|
+
}
|
|
4598
|
+
logger.log("");
|
|
4599
|
+
}
|
|
4600
|
+
} catch {
|
|
4601
|
+
}
|
|
4602
|
+
logger.info("Specify source explicitly:");
|
|
4603
|
+
logger.info(` ${theme.command("bragduck sync --source <type>")}`);
|
|
4300
4604
|
logger.log("");
|
|
4301
|
-
logger.info("
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
if (!AdapterFactory.isSupported(sourceType)) {
|
|
4305
|
-
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, `Source ${sourceType} not yet supported`);
|
|
4306
|
-
logger.log("");
|
|
4307
|
-
logger.info(`Currently supported: GitHub, GitLab, Bitbucket, Jira, Confluence`);
|
|
4605
|
+
logger.info("Supported sources: github, gitlab, bitbucket, jira, confluence");
|
|
4308
4606
|
return;
|
|
4309
4607
|
}
|
|
4310
4608
|
if (sourceType === "jira" || sourceType === "confluence") {
|
|
4311
4609
|
const creds = await storageService.getServiceCredentials(sourceType);
|
|
4312
|
-
|
|
4610
|
+
const envInstance = loadEnvConfig()[`${sourceType}Instance`];
|
|
4611
|
+
const projectConfig = await findProjectConfig();
|
|
4612
|
+
const configInstance = projectConfig?.[`${sourceType}Instance`];
|
|
4613
|
+
if (!creds?.instanceUrl && !envInstance && !configInstance) {
|
|
4313
4614
|
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, `No ${sourceType} instance configured`);
|
|
4314
4615
|
logger.log("");
|
|
4315
|
-
logger.info(
|
|
4616
|
+
logger.info("Configure instance via:");
|
|
4617
|
+
logger.info(` ${theme.command("bragduck auth atlassian")} (interactive)`);
|
|
4618
|
+
logger.info(
|
|
4619
|
+
` ${theme.command(`bragduck config set ${sourceType}Instance company.atlassian.net`)}`
|
|
4620
|
+
);
|
|
4621
|
+
logger.info(
|
|
4622
|
+
` ${theme.command(`export BRAGDUCK_${sourceType.toUpperCase()}_INSTANCE=company.atlassian.net`)}`
|
|
4623
|
+
);
|
|
4316
4624
|
return;
|
|
4317
4625
|
}
|
|
4318
4626
|
}
|
|
@@ -5062,7 +5370,12 @@ async function configCommand(subcommand, key, value) {
|
|
|
5062
5370
|
async function handleListConfig() {
|
|
5063
5371
|
const config2 = {
|
|
5064
5372
|
defaultCommitDays: storageService.getConfig("defaultCommitDays"),
|
|
5065
|
-
autoVersionCheck: storageService.getConfig("autoVersionCheck")
|
|
5373
|
+
autoVersionCheck: storageService.getConfig("autoVersionCheck"),
|
|
5374
|
+
defaultSource: storageService.getConfig("defaultSource"),
|
|
5375
|
+
sourcePriority: storageService.getConfig("sourcePriority"),
|
|
5376
|
+
jiraInstance: storageService.getConfig("jiraInstance"),
|
|
5377
|
+
confluenceInstance: storageService.getConfig("confluenceInstance"),
|
|
5378
|
+
gitlabInstance: storageService.getConfig("gitlabInstance")
|
|
5066
5379
|
};
|
|
5067
5380
|
const table = new Table3({
|
|
5068
5381
|
head: [chalk8.cyan("Key"), chalk8.cyan("Value"), chalk8.cyan("Default")],
|
|
@@ -5083,6 +5396,31 @@ async function handleListConfig() {
|
|
|
5083
5396
|
chalk8.yellow(String(config2.autoVersionCheck)),
|
|
5084
5397
|
chalk8.dim(String(DEFAULT_CONFIG.autoVersionCheck))
|
|
5085
5398
|
]);
|
|
5399
|
+
table.push([
|
|
5400
|
+
chalk8.white("defaultSource"),
|
|
5401
|
+
chalk8.yellow(config2.defaultSource || "not set"),
|
|
5402
|
+
chalk8.dim("not set")
|
|
5403
|
+
]);
|
|
5404
|
+
table.push([
|
|
5405
|
+
chalk8.white("sourcePriority"),
|
|
5406
|
+
chalk8.yellow(config2.sourcePriority ? config2.sourcePriority.join(", ") : "not set"),
|
|
5407
|
+
chalk8.dim("not set")
|
|
5408
|
+
]);
|
|
5409
|
+
table.push([
|
|
5410
|
+
chalk8.white("jiraInstance"),
|
|
5411
|
+
chalk8.yellow(config2.jiraInstance || "not set"),
|
|
5412
|
+
chalk8.dim("not set")
|
|
5413
|
+
]);
|
|
5414
|
+
table.push([
|
|
5415
|
+
chalk8.white("confluenceInstance"),
|
|
5416
|
+
chalk8.yellow(config2.confluenceInstance || "not set"),
|
|
5417
|
+
chalk8.dim("not set")
|
|
5418
|
+
]);
|
|
5419
|
+
table.push([
|
|
5420
|
+
chalk8.white("gitlabInstance"),
|
|
5421
|
+
chalk8.yellow(config2.gitlabInstance || "not set"),
|
|
5422
|
+
chalk8.dim("not set")
|
|
5423
|
+
]);
|
|
5086
5424
|
logger.info("Current configuration:");
|
|
5087
5425
|
logger.log("");
|
|
5088
5426
|
logger.log(table.toString());
|
|
@@ -5162,6 +5500,43 @@ Must be a number between 1 and 365`
|
|
|
5162
5500
|
Must be one of: true, false, yes, no, 1, 0`
|
|
5163
5501
|
);
|
|
5164
5502
|
}
|
|
5503
|
+
case CONFIG_KEYS.DEFAULT_SOURCE: {
|
|
5504
|
+
const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
|
|
5505
|
+
if (!validSources.includes(value)) {
|
|
5506
|
+
throw new ValidationError(
|
|
5507
|
+
`Invalid source: "${value}"
|
|
5508
|
+
|
|
5509
|
+
Valid sources: ${validSources.join(", ")}`
|
|
5510
|
+
);
|
|
5511
|
+
}
|
|
5512
|
+
return value;
|
|
5513
|
+
}
|
|
5514
|
+
case CONFIG_KEYS.SOURCE_PRIORITY: {
|
|
5515
|
+
const sources = value.split(",").map((s) => s.trim());
|
|
5516
|
+
const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
|
|
5517
|
+
for (const source of sources) {
|
|
5518
|
+
if (!validSources.includes(source)) {
|
|
5519
|
+
throw new ValidationError(
|
|
5520
|
+
`Invalid source in priority list: "${source}"
|
|
5521
|
+
|
|
5522
|
+
Valid sources: ${validSources.join(", ")}`
|
|
5523
|
+
);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
return sources;
|
|
5527
|
+
}
|
|
5528
|
+
case CONFIG_KEYS.JIRA_INSTANCE:
|
|
5529
|
+
case CONFIG_KEYS.CONFLUENCE_INSTANCE:
|
|
5530
|
+
case CONFIG_KEYS.GITLAB_INSTANCE: {
|
|
5531
|
+
if (!value.match(/^[a-zA-Z0-9.-]+(:[0-9]+)?(\/.*)?$/)) {
|
|
5532
|
+
throw new ValidationError(
|
|
5533
|
+
`Invalid instance URL: "${value}"
|
|
5534
|
+
|
|
5535
|
+
Must be a valid hostname (e.g., company.atlassian.net, gitlab.company.com)`
|
|
5536
|
+
);
|
|
5537
|
+
}
|
|
5538
|
+
return value;
|
|
5539
|
+
}
|
|
5165
5540
|
default:
|
|
5166
5541
|
throw new ValidationError(`Unknown config key: "${key}"`);
|
|
5167
5542
|
}
|