@pilatos/bitbucket-cli 1.8.0 → 1.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/index.js +322 -81
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -44211,7 +44211,7 @@ var ServiceTokens = {
|
|
|
44211
44211
|
};
|
|
44212
44212
|
|
|
44213
44213
|
// src/services/config.service.ts
|
|
44214
|
-
import { join } from "path";
|
|
44214
|
+
import { join, win32 } from "path";
|
|
44215
44215
|
import { homedir } from "os";
|
|
44216
44216
|
|
|
44217
44217
|
// src/types/errors.ts
|
|
@@ -44282,9 +44282,24 @@ class ConfigService {
|
|
|
44282
44282
|
configDir;
|
|
44283
44283
|
configFile;
|
|
44284
44284
|
configCache = null;
|
|
44285
|
-
constructor(configDir) {
|
|
44286
|
-
|
|
44287
|
-
|
|
44285
|
+
constructor(configDir, options = {}) {
|
|
44286
|
+
const platform = options.platform ?? process.platform;
|
|
44287
|
+
const joinPath = platform === "win32" ? win32.join : join;
|
|
44288
|
+
this.configDir = configDir ?? this.resolveDefaultConfigDir({ ...options, platform });
|
|
44289
|
+
this.configFile = joinPath(this.configDir, "config.json");
|
|
44290
|
+
}
|
|
44291
|
+
resolveDefaultConfigDir(options) {
|
|
44292
|
+
const platform = options.platform ?? process.platform;
|
|
44293
|
+
if (platform === "win32") {
|
|
44294
|
+
const appDataDir = options.appData ?? process.env.APPDATA;
|
|
44295
|
+
if (appDataDir) {
|
|
44296
|
+
return win32.join(appDataDir, "bb");
|
|
44297
|
+
}
|
|
44298
|
+
const homeDir2 = options.homeDir ?? homedir();
|
|
44299
|
+
return win32.join(homeDir2, "AppData", "Roaming", "bb");
|
|
44300
|
+
}
|
|
44301
|
+
const homeDir = options.homeDir ?? homedir();
|
|
44302
|
+
return join(homeDir, ".config", "bb");
|
|
44288
44303
|
}
|
|
44289
44304
|
async ensureConfigDir() {
|
|
44290
44305
|
try {
|
|
@@ -44354,6 +44369,11 @@ class ConfigService {
|
|
|
44354
44369
|
apiToken: credentials.apiToken
|
|
44355
44370
|
});
|
|
44356
44371
|
}
|
|
44372
|
+
async clearCredentials() {
|
|
44373
|
+
const config = await this.getConfig();
|
|
44374
|
+
const { username: _username, apiToken: _apiToken, ...rest } = config;
|
|
44375
|
+
await this.setConfig(rest);
|
|
44376
|
+
}
|
|
44357
44377
|
async clearConfig() {
|
|
44358
44378
|
this.configCache = null;
|
|
44359
44379
|
await this.setConfig({});
|
|
@@ -45018,6 +45038,9 @@ class OutputService {
|
|
|
45018
45038
|
json(data) {
|
|
45019
45039
|
console.log(JSON.stringify(data, null, 2));
|
|
45020
45040
|
}
|
|
45041
|
+
jsonError(data) {
|
|
45042
|
+
console.error(JSON.stringify(data));
|
|
45043
|
+
}
|
|
45021
45044
|
table(headers, rows) {
|
|
45022
45045
|
if (rows.length === 0) {
|
|
45023
45046
|
return;
|
|
@@ -45149,14 +45172,22 @@ function parseSettableConfigValue(key, value) {
|
|
|
45149
45172
|
case "skipVersionCheck": {
|
|
45150
45173
|
const parsed = parseBooleanLiteral(value);
|
|
45151
45174
|
if (parsed === undefined) {
|
|
45152
|
-
throw new
|
|
45175
|
+
throw new BBError({
|
|
45176
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
45177
|
+
message: "Invalid value for 'skipVersionCheck'. Expected 'true' or 'false'.",
|
|
45178
|
+
context: { key, value }
|
|
45179
|
+
});
|
|
45153
45180
|
}
|
|
45154
45181
|
return parsed;
|
|
45155
45182
|
}
|
|
45156
45183
|
case "versionCheckInterval": {
|
|
45157
45184
|
const parsed = parsePositiveIntegerLiteral(value);
|
|
45158
45185
|
if (parsed === undefined) {
|
|
45159
|
-
throw new
|
|
45186
|
+
throw new BBError({
|
|
45187
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
45188
|
+
message: "Invalid value for 'versionCheckInterval'. Expected a positive integer (1 or greater).",
|
|
45189
|
+
context: { key, value }
|
|
45190
|
+
});
|
|
45160
45191
|
}
|
|
45161
45192
|
return parsed;
|
|
45162
45193
|
}
|
|
@@ -51614,7 +51645,9 @@ class BaseCommand {
|
|
|
51614
51645
|
}
|
|
51615
51646
|
}
|
|
51616
51647
|
handleError(error, context) {
|
|
51617
|
-
if (
|
|
51648
|
+
if (context.globalOptions.json) {
|
|
51649
|
+
this.output.jsonError(this.normalizeErrorForJson(error));
|
|
51650
|
+
} else if (error instanceof Error) {
|
|
51618
51651
|
this.output.error(error.message);
|
|
51619
51652
|
} else {
|
|
51620
51653
|
this.output.error(String(error));
|
|
@@ -51623,6 +51656,23 @@ class BaseCommand {
|
|
|
51623
51656
|
process.exitCode = 1;
|
|
51624
51657
|
}
|
|
51625
51658
|
}
|
|
51659
|
+
normalizeErrorForJson(error) {
|
|
51660
|
+
if (error instanceof BBError) {
|
|
51661
|
+
return error.toJSON();
|
|
51662
|
+
}
|
|
51663
|
+
if (error instanceof Error) {
|
|
51664
|
+
return {
|
|
51665
|
+
name: error.name,
|
|
51666
|
+
code: 9999 /* UNKNOWN */,
|
|
51667
|
+
message: error.message
|
|
51668
|
+
};
|
|
51669
|
+
}
|
|
51670
|
+
return {
|
|
51671
|
+
name: "Error",
|
|
51672
|
+
code: 9999 /* UNKNOWN */,
|
|
51673
|
+
message: String(error)
|
|
51674
|
+
};
|
|
51675
|
+
}
|
|
51626
51676
|
requireOption(value, name, message) {
|
|
51627
51677
|
if (value === undefined || value === null || value === "") {
|
|
51628
51678
|
throw new BBError({
|
|
@@ -51649,10 +51699,16 @@ class LoginCommand extends BaseCommand {
|
|
|
51649
51699
|
const username = options.username || process.env.BB_USERNAME;
|
|
51650
51700
|
const apiToken = options.password || process.env.BB_API_TOKEN;
|
|
51651
51701
|
if (!username) {
|
|
51652
|
-
throw new
|
|
51702
|
+
throw new BBError({
|
|
51703
|
+
code: 5001 /* VALIDATION_REQUIRED */,
|
|
51704
|
+
message: "Username is required. Use --username option or set BB_USERNAME environment variable."
|
|
51705
|
+
});
|
|
51653
51706
|
}
|
|
51654
51707
|
if (!apiToken) {
|
|
51655
|
-
throw new
|
|
51708
|
+
throw new BBError({
|
|
51709
|
+
code: 5001 /* VALIDATION_REQUIRED */,
|
|
51710
|
+
message: "API token is required. Use --password option or set BB_API_TOKEN environment variable."
|
|
51711
|
+
});
|
|
51656
51712
|
}
|
|
51657
51713
|
await this.configService.setCredentials({ username, apiToken });
|
|
51658
51714
|
try {
|
|
@@ -51671,8 +51727,11 @@ class LoginCommand extends BaseCommand {
|
|
|
51671
51727
|
}
|
|
51672
51728
|
this.output.success(`Logged in as ${user.display_name} (${user.username})`);
|
|
51673
51729
|
} catch (error) {
|
|
51674
|
-
await this.configService.
|
|
51675
|
-
throw new
|
|
51730
|
+
await this.configService.clearCredentials();
|
|
51731
|
+
throw new BBError({
|
|
51732
|
+
code: 1002 /* AUTH_INVALID */,
|
|
51733
|
+
message: `Authentication failed: ${error instanceof Error ? error.message : String(error)}`
|
|
51734
|
+
});
|
|
51676
51735
|
}
|
|
51677
51736
|
}
|
|
51678
51737
|
}
|
|
@@ -51687,7 +51746,7 @@ class LogoutCommand extends BaseCommand {
|
|
|
51687
51746
|
this.configService = configService;
|
|
51688
51747
|
}
|
|
51689
51748
|
async execute(_options, context) {
|
|
51690
|
-
await this.configService.
|
|
51749
|
+
await this.configService.clearCredentials();
|
|
51691
51750
|
if (context.globalOptions.json) {
|
|
51692
51751
|
this.output.json({ authenticated: false, success: true });
|
|
51693
51752
|
return;
|
|
@@ -51741,7 +51800,11 @@ class StatusCommand extends BaseCommand {
|
|
|
51741
51800
|
this.output.text(` Default workspace: ${this.output.highlight(config.defaultWorkspace)}`);
|
|
51742
51801
|
}
|
|
51743
51802
|
} catch (error) {
|
|
51744
|
-
throw new
|
|
51803
|
+
throw new BBError({
|
|
51804
|
+
code: 1002 /* AUTH_INVALID */,
|
|
51805
|
+
message: `Authentication is invalid or expired. Run ${this.output.highlight("bb auth login")} to re-authenticate.`,
|
|
51806
|
+
cause: error instanceof Error ? error : undefined
|
|
51807
|
+
});
|
|
51745
51808
|
}
|
|
51746
51809
|
}
|
|
51747
51810
|
}
|
|
@@ -51758,7 +51821,10 @@ class TokenCommand extends BaseCommand {
|
|
|
51758
51821
|
async execute(_options, context) {
|
|
51759
51822
|
const credentials = await this.configService.getCredentials();
|
|
51760
51823
|
if (!credentials.username || !credentials.apiToken) {
|
|
51761
|
-
throw new
|
|
51824
|
+
throw new BBError({
|
|
51825
|
+
code: 1001 /* AUTH_REQUIRED */,
|
|
51826
|
+
message: "Not authenticated. Run 'bb auth login' first."
|
|
51827
|
+
});
|
|
51762
51828
|
}
|
|
51763
51829
|
const token = Buffer.from(`${credentials.username}:${credentials.apiToken}`).toString("base64");
|
|
51764
51830
|
if (context.globalOptions.json) {
|
|
@@ -51806,7 +51872,10 @@ class CloneCommand extends BaseCommand {
|
|
|
51806
51872
|
if (parts.length === 1) {
|
|
51807
51873
|
const config = await this.configService.getConfig();
|
|
51808
51874
|
if (!config.defaultWorkspace) {
|
|
51809
|
-
throw new
|
|
51875
|
+
throw new BBError({
|
|
51876
|
+
code: 6002 /* CONTEXT_WORKSPACE_NOT_FOUND */,
|
|
51877
|
+
message: "No workspace specified. Use workspace/repo format or set a default workspace."
|
|
51878
|
+
});
|
|
51810
51879
|
}
|
|
51811
51880
|
workspace = config.defaultWorkspace;
|
|
51812
51881
|
repoSlug = parts[0];
|
|
@@ -51814,7 +51883,11 @@ class CloneCommand extends BaseCommand {
|
|
|
51814
51883
|
workspace = parts[0];
|
|
51815
51884
|
repoSlug = parts[1];
|
|
51816
51885
|
} else {
|
|
51817
|
-
throw new
|
|
51886
|
+
throw new BBError({
|
|
51887
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
51888
|
+
message: "Invalid repository format. Use workspace/repo or a full URL.",
|
|
51889
|
+
context: { repository }
|
|
51890
|
+
});
|
|
51818
51891
|
}
|
|
51819
51892
|
return `git@bitbucket.org:${workspace}/${repoSlug}.git`;
|
|
51820
51893
|
}
|
|
@@ -51822,7 +51895,11 @@ class CloneCommand extends BaseCommand {
|
|
|
51822
51895
|
const parts = repository.split("/");
|
|
51823
51896
|
const lastPart = parts.at(-1);
|
|
51824
51897
|
if (!lastPart) {
|
|
51825
|
-
throw new
|
|
51898
|
+
throw new BBError({
|
|
51899
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
51900
|
+
message: "Invalid repository format.",
|
|
51901
|
+
context: { repository }
|
|
51902
|
+
});
|
|
51826
51903
|
}
|
|
51827
51904
|
return lastPart.replace(".git", "");
|
|
51828
51905
|
}
|
|
@@ -51878,12 +51955,61 @@ class CreateRepoCommand extends BaseCommand {
|
|
|
51878
51955
|
}
|
|
51879
51956
|
const config = await this.configService.getConfig();
|
|
51880
51957
|
if (!config.defaultWorkspace) {
|
|
51881
|
-
throw new
|
|
51958
|
+
throw new BBError({
|
|
51959
|
+
code: 6002 /* CONTEXT_WORKSPACE_NOT_FOUND */,
|
|
51960
|
+
message: "No workspace specified. Use --workspace option or set a default workspace."
|
|
51961
|
+
});
|
|
51882
51962
|
}
|
|
51883
51963
|
return config.defaultWorkspace;
|
|
51884
51964
|
}
|
|
51885
51965
|
}
|
|
51886
51966
|
|
|
51967
|
+
// src/services/pagination.ts
|
|
51968
|
+
var DEFAULT_LIMIT = 25;
|
|
51969
|
+
var MAX_PAGE_LENGTH = 50;
|
|
51970
|
+
function parseLimit(limit, fallback = DEFAULT_LIMIT) {
|
|
51971
|
+
if (!limit) {
|
|
51972
|
+
return fallback;
|
|
51973
|
+
}
|
|
51974
|
+
const parsed = Number.parseInt(limit, 10);
|
|
51975
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
51976
|
+
return fallback;
|
|
51977
|
+
}
|
|
51978
|
+
return parsed;
|
|
51979
|
+
}
|
|
51980
|
+
async function collectPages(options) {
|
|
51981
|
+
const { fetchPage, shouldInclude } = options;
|
|
51982
|
+
const limit = Math.max(0, options.limit);
|
|
51983
|
+
if (limit === 0) {
|
|
51984
|
+
return [];
|
|
51985
|
+
}
|
|
51986
|
+
const requestedPageSize = options.pageSize ?? limit;
|
|
51987
|
+
const pagelen = Math.max(1, Math.min(requestedPageSize, MAX_PAGE_LENGTH));
|
|
51988
|
+
const items = [];
|
|
51989
|
+
let page = 1;
|
|
51990
|
+
while (items.length < limit) {
|
|
51991
|
+
const data = await fetchPage(page, pagelen);
|
|
51992
|
+
const pageValues = data.values ? Array.from(data.values) : [];
|
|
51993
|
+
if (pageValues.length === 0) {
|
|
51994
|
+
break;
|
|
51995
|
+
}
|
|
51996
|
+
for (const value of pageValues) {
|
|
51997
|
+
if (shouldInclude && !shouldInclude(value)) {
|
|
51998
|
+
continue;
|
|
51999
|
+
}
|
|
52000
|
+
items.push(value);
|
|
52001
|
+
if (items.length >= limit) {
|
|
52002
|
+
return items;
|
|
52003
|
+
}
|
|
52004
|
+
}
|
|
52005
|
+
if (!data.next) {
|
|
52006
|
+
break;
|
|
52007
|
+
}
|
|
52008
|
+
page += 1;
|
|
52009
|
+
}
|
|
52010
|
+
return items;
|
|
52011
|
+
}
|
|
52012
|
+
|
|
51887
52013
|
// src/commands/repo/list.command.ts
|
|
51888
52014
|
class ListReposCommand extends BaseCommand {
|
|
51889
52015
|
repositoriesApi;
|
|
@@ -51897,11 +52023,18 @@ class ListReposCommand extends BaseCommand {
|
|
|
51897
52023
|
}
|
|
51898
52024
|
async execute(options, context) {
|
|
51899
52025
|
const workspace = await this.resolveWorkspace(options.workspace ?? context.globalOptions.workspace);
|
|
51900
|
-
const limit =
|
|
51901
|
-
const
|
|
51902
|
-
|
|
52026
|
+
const limit = parseLimit(options.limit);
|
|
52027
|
+
const repos = await collectPages({
|
|
52028
|
+
limit,
|
|
52029
|
+
fetchPage: async (page, pagelen) => {
|
|
52030
|
+
const response = await this.repositoriesApi.repositoriesWorkspaceGet({
|
|
52031
|
+
workspace
|
|
52032
|
+
}, {
|
|
52033
|
+
params: { page, pagelen }
|
|
52034
|
+
});
|
|
52035
|
+
return response.data;
|
|
52036
|
+
}
|
|
51903
52037
|
});
|
|
51904
|
-
const repos = Array.from(response.data.values ?? []).slice(0, limit);
|
|
51905
52038
|
if (context.globalOptions.json) {
|
|
51906
52039
|
this.output.json({
|
|
51907
52040
|
workspace,
|
|
@@ -51927,7 +52060,10 @@ class ListReposCommand extends BaseCommand {
|
|
|
51927
52060
|
}
|
|
51928
52061
|
const config = await this.configService.getConfig();
|
|
51929
52062
|
if (!config.defaultWorkspace) {
|
|
51930
|
-
throw new
|
|
52063
|
+
throw new BBError({
|
|
52064
|
+
code: 6002 /* CONTEXT_WORKSPACE_NOT_FOUND */,
|
|
52065
|
+
message: "No workspace specified. Use --workspace option or set a default workspace."
|
|
52066
|
+
});
|
|
51931
52067
|
}
|
|
51932
52068
|
return config.defaultWorkspace;
|
|
51933
52069
|
}
|
|
@@ -52012,8 +52148,11 @@ class DeleteRepoCommand extends BaseCommand {
|
|
|
52012
52148
|
}
|
|
52013
52149
|
const repoContext = await this.contextService.requireRepoContext(contextOptions);
|
|
52014
52150
|
if (!yes) {
|
|
52015
|
-
throw new
|
|
52016
|
-
|
|
52151
|
+
throw new BBError({
|
|
52152
|
+
code: 5001 /* VALIDATION_REQUIRED */,
|
|
52153
|
+
message: `This will permanently delete ${repoContext.workspace}/${repoContext.repoSlug}.
|
|
52154
|
+
` + "Use --yes to confirm deletion."
|
|
52155
|
+
});
|
|
52017
52156
|
}
|
|
52018
52157
|
await this.repositoriesApi.repositoriesWorkspaceRepoSlugDelete({
|
|
52019
52158
|
workspace: repoContext.workspace,
|
|
@@ -52046,7 +52185,10 @@ class CreatePRCommand extends BaseCommand {
|
|
|
52046
52185
|
}
|
|
52047
52186
|
async execute(options, context) {
|
|
52048
52187
|
if (!options.title) {
|
|
52049
|
-
throw new
|
|
52188
|
+
throw new BBError({
|
|
52189
|
+
code: 5001 /* VALIDATION_REQUIRED */,
|
|
52190
|
+
message: "Pull request title is required. Use --title option."
|
|
52191
|
+
});
|
|
52050
52192
|
}
|
|
52051
52193
|
const repoContext = await this.contextService.requireRepoContext({
|
|
52052
52194
|
...context.globalOptions,
|
|
@@ -52110,13 +52252,20 @@ class ListPRsCommand extends BaseCommand {
|
|
|
52110
52252
|
...options
|
|
52111
52253
|
});
|
|
52112
52254
|
const state = options.state || "OPEN";
|
|
52113
|
-
const
|
|
52114
|
-
|
|
52115
|
-
|
|
52116
|
-
|
|
52255
|
+
const limit = parseLimit(options.limit);
|
|
52256
|
+
const values = await collectPages({
|
|
52257
|
+
limit,
|
|
52258
|
+
fetchPage: async (page, pagelen) => {
|
|
52259
|
+
const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsGet({
|
|
52260
|
+
workspace: repoContext.workspace,
|
|
52261
|
+
repoSlug: repoContext.repoSlug,
|
|
52262
|
+
state
|
|
52263
|
+
}, {
|
|
52264
|
+
params: { page, pagelen }
|
|
52265
|
+
});
|
|
52266
|
+
return response.data;
|
|
52267
|
+
}
|
|
52117
52268
|
});
|
|
52118
|
-
const data = response.data;
|
|
52119
|
-
const values = data.values ? Array.from(data.values) : [];
|
|
52120
52269
|
if (context.globalOptions.json) {
|
|
52121
52270
|
this.output.json({
|
|
52122
52271
|
workspace: repoContext.workspace,
|
|
@@ -52299,18 +52448,31 @@ class EditPRCommand extends BaseCommand {
|
|
|
52299
52448
|
prId = Number.parseInt(options.id, 10);
|
|
52300
52449
|
} else {
|
|
52301
52450
|
const currentBranch = await this.gitService.getCurrentBranch();
|
|
52302
|
-
const
|
|
52303
|
-
|
|
52304
|
-
|
|
52305
|
-
|
|
52306
|
-
|
|
52307
|
-
|
|
52308
|
-
|
|
52309
|
-
|
|
52310
|
-
|
|
52451
|
+
const matches = await collectPages({
|
|
52452
|
+
limit: 1,
|
|
52453
|
+
pageSize: MAX_PAGE_LENGTH,
|
|
52454
|
+
fetchPage: async (page, pagelen) => {
|
|
52455
|
+
const response2 = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsGet({
|
|
52456
|
+
workspace: repoContext.workspace,
|
|
52457
|
+
repoSlug: repoContext.repoSlug,
|
|
52458
|
+
state: "OPEN"
|
|
52459
|
+
}, {
|
|
52460
|
+
params: { page, pagelen }
|
|
52461
|
+
});
|
|
52462
|
+
return response2.data;
|
|
52463
|
+
},
|
|
52464
|
+
shouldInclude: (pullRequest) => {
|
|
52465
|
+
const source = pullRequest.source;
|
|
52466
|
+
return source?.branch?.name === currentBranch;
|
|
52467
|
+
}
|
|
52311
52468
|
});
|
|
52469
|
+
const matchingPR = matches[0];
|
|
52312
52470
|
if (!matchingPR) {
|
|
52313
|
-
throw new
|
|
52471
|
+
throw new BBError({
|
|
52472
|
+
code: 2002 /* API_NOT_FOUND */,
|
|
52473
|
+
message: `No open pull request found for current branch '${currentBranch}'. Specify a PR ID explicitly.`,
|
|
52474
|
+
context: { branch: currentBranch }
|
|
52475
|
+
});
|
|
52314
52476
|
}
|
|
52315
52477
|
prId = matchingPR.id;
|
|
52316
52478
|
}
|
|
@@ -52319,11 +52481,19 @@ class EditPRCommand extends BaseCommand {
|
|
|
52319
52481
|
try {
|
|
52320
52482
|
body = fs.readFileSync(options.bodyFile, "utf-8");
|
|
52321
52483
|
} catch (err) {
|
|
52322
|
-
throw new
|
|
52484
|
+
throw new BBError({
|
|
52485
|
+
code: 9999 /* UNKNOWN */,
|
|
52486
|
+
message: `Failed to read file '${options.bodyFile}': ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
52487
|
+
cause: err instanceof Error ? err : undefined,
|
|
52488
|
+
context: { bodyFile: options.bodyFile }
|
|
52489
|
+
});
|
|
52323
52490
|
}
|
|
52324
52491
|
}
|
|
52325
52492
|
if (!options.title && !body) {
|
|
52326
|
-
throw new
|
|
52493
|
+
throw new BBError({
|
|
52494
|
+
code: 5001 /* VALIDATION_REQUIRED */,
|
|
52495
|
+
message: "At least one of --title or --body (or --body-file) is required."
|
|
52496
|
+
});
|
|
52327
52497
|
}
|
|
52328
52498
|
const request = {
|
|
52329
52499
|
type: "pullrequest"
|
|
@@ -52540,7 +52710,11 @@ class CheckoutPRCommand extends BaseCommand {
|
|
|
52540
52710
|
const branchName = pr.source?.branch?.name;
|
|
52541
52711
|
const localBranchName = `pr-${prId}`;
|
|
52542
52712
|
if (!branchName) {
|
|
52543
|
-
throw new
|
|
52713
|
+
throw new BBError({
|
|
52714
|
+
code: 2002 /* API_NOT_FOUND */,
|
|
52715
|
+
message: "Pull request source branch not found",
|
|
52716
|
+
context: { pullRequestId: prId }
|
|
52717
|
+
});
|
|
52544
52718
|
}
|
|
52545
52719
|
await this.gitService.fetch();
|
|
52546
52720
|
let checkedOutBranch;
|
|
@@ -52601,18 +52775,39 @@ class DiffPRCommand extends BaseCommand {
|
|
|
52601
52775
|
if (options.id) {
|
|
52602
52776
|
prId = Number.parseInt(options.id, 10);
|
|
52603
52777
|
if (Number.isNaN(prId)) {
|
|
52604
|
-
throw new
|
|
52778
|
+
throw new BBError({
|
|
52779
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
52780
|
+
message: "Invalid PR ID",
|
|
52781
|
+
context: { id: options.id }
|
|
52782
|
+
});
|
|
52605
52783
|
}
|
|
52606
52784
|
} else {
|
|
52607
52785
|
const currentBranch = await this.gitService.getCurrentBranch();
|
|
52608
|
-
const
|
|
52609
|
-
|
|
52610
|
-
|
|
52611
|
-
|
|
52786
|
+
const matches = await collectPages({
|
|
52787
|
+
limit: 1,
|
|
52788
|
+
pageSize: MAX_PAGE_LENGTH,
|
|
52789
|
+
fetchPage: async (page, pagelen) => {
|
|
52790
|
+
const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsGet({
|
|
52791
|
+
workspace: repoContext.workspace,
|
|
52792
|
+
repoSlug: repoContext.repoSlug,
|
|
52793
|
+
state: "OPEN"
|
|
52794
|
+
}, {
|
|
52795
|
+
params: { page, pagelen }
|
|
52796
|
+
});
|
|
52797
|
+
return response.data;
|
|
52798
|
+
},
|
|
52799
|
+
shouldInclude: (pullRequest) => {
|
|
52800
|
+
const source = pullRequest.source;
|
|
52801
|
+
return source?.branch?.name === currentBranch;
|
|
52802
|
+
}
|
|
52612
52803
|
});
|
|
52613
|
-
const pr =
|
|
52804
|
+
const pr = matches[0];
|
|
52614
52805
|
if (!pr) {
|
|
52615
|
-
throw new
|
|
52806
|
+
throw new BBError({
|
|
52807
|
+
code: 2002 /* API_NOT_FOUND */,
|
|
52808
|
+
message: `No open pull request found for branch "${currentBranch}"`,
|
|
52809
|
+
context: { branch: currentBranch }
|
|
52810
|
+
});
|
|
52616
52811
|
}
|
|
52617
52812
|
prId = pr.id;
|
|
52618
52813
|
}
|
|
@@ -52687,11 +52882,13 @@ class DiffPRCommand extends BaseCommand {
|
|
|
52687
52882
|
repoSlug,
|
|
52688
52883
|
pullRequestId: prId
|
|
52689
52884
|
});
|
|
52690
|
-
const
|
|
52691
|
-
|
|
52692
|
-
|
|
52885
|
+
const links = prResponse.data.links;
|
|
52886
|
+
const htmlUrl = links?.html?.href;
|
|
52887
|
+
if (htmlUrl) {
|
|
52888
|
+
const normalizedHtmlUrl = htmlUrl.replace(/\/$/, "");
|
|
52889
|
+
return `${normalizedHtmlUrl}/diff`;
|
|
52693
52890
|
}
|
|
52694
|
-
return
|
|
52891
|
+
return `https://bitbucket.org/${workspace}/${repoSlug}/pull-requests/${prId}/diff`;
|
|
52695
52892
|
}
|
|
52696
52893
|
async showStat(workspace, repoSlug, prId, useJson) {
|
|
52697
52894
|
const diffstatResponse = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdDiffstatGet({
|
|
@@ -52814,15 +53011,28 @@ class ActivityPRCommand extends BaseCommand {
|
|
|
52814
53011
|
...options
|
|
52815
53012
|
});
|
|
52816
53013
|
const prId = Number.parseInt(options.id, 10);
|
|
52817
|
-
const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdActivityGet({
|
|
52818
|
-
workspace: repoContext.workspace,
|
|
52819
|
-
repoSlug: repoContext.repoSlug,
|
|
52820
|
-
pullRequestId: prId
|
|
52821
|
-
});
|
|
52822
|
-
const data = response.data;
|
|
52823
|
-
const values = data?.values ? Array.from(data.values) : [];
|
|
52824
53014
|
const filterTypes = this.parseTypeFilter(options.type);
|
|
52825
|
-
const
|
|
53015
|
+
const limit = parseLimit(options.limit);
|
|
53016
|
+
const activities = await collectPages({
|
|
53017
|
+
limit,
|
|
53018
|
+
fetchPage: async (page, pagelen) => {
|
|
53019
|
+
const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdActivityGet({
|
|
53020
|
+
workspace: repoContext.workspace,
|
|
53021
|
+
repoSlug: repoContext.repoSlug,
|
|
53022
|
+
pullRequestId: prId
|
|
53023
|
+
}, {
|
|
53024
|
+
params: { page, pagelen }
|
|
53025
|
+
});
|
|
53026
|
+
return response.data;
|
|
53027
|
+
},
|
|
53028
|
+
shouldInclude: (activity) => {
|
|
53029
|
+
if (filterTypes.length === 0) {
|
|
53030
|
+
return true;
|
|
53031
|
+
}
|
|
53032
|
+
return filterTypes.includes(this.getActivityType(activity));
|
|
53033
|
+
}
|
|
53034
|
+
});
|
|
53035
|
+
const typedActivities = activities;
|
|
52826
53036
|
if (context.globalOptions.json) {
|
|
52827
53037
|
this.output.json({
|
|
52828
53038
|
workspace: repoContext.workspace,
|
|
@@ -52831,12 +53041,12 @@ class ActivityPRCommand extends BaseCommand {
|
|
|
52831
53041
|
filters: {
|
|
52832
53042
|
types: filterTypes
|
|
52833
53043
|
},
|
|
52834
|
-
count:
|
|
52835
|
-
activities
|
|
53044
|
+
count: typedActivities.length,
|
|
53045
|
+
activities: typedActivities
|
|
52836
53046
|
});
|
|
52837
53047
|
return;
|
|
52838
53048
|
}
|
|
52839
|
-
if (
|
|
53049
|
+
if (typedActivities.length === 0) {
|
|
52840
53050
|
if (filterTypes.length > 0) {
|
|
52841
53051
|
this.output.info("No activity entries matched the requested filter");
|
|
52842
53052
|
} else {
|
|
@@ -52844,7 +53054,7 @@ class ActivityPRCommand extends BaseCommand {
|
|
|
52844
53054
|
}
|
|
52845
53055
|
return;
|
|
52846
53056
|
}
|
|
52847
|
-
const rows =
|
|
53057
|
+
const rows = typedActivities.map((activity) => {
|
|
52848
53058
|
const activityType = this.getActivityType(activity);
|
|
52849
53059
|
return [
|
|
52850
53060
|
activityType.toUpperCase(),
|
|
@@ -53055,13 +53265,20 @@ class ListCommentsPRCommand extends BaseCommand {
|
|
|
53055
53265
|
...options
|
|
53056
53266
|
});
|
|
53057
53267
|
const prId = Number.parseInt(options.id, 10);
|
|
53058
|
-
const
|
|
53059
|
-
|
|
53060
|
-
|
|
53061
|
-
|
|
53268
|
+
const limit = parseLimit(options.limit);
|
|
53269
|
+
const values = await collectPages({
|
|
53270
|
+
limit,
|
|
53271
|
+
fetchPage: async (page, pagelen) => {
|
|
53272
|
+
const response = await this.pullrequestsApi.repositoriesWorkspaceRepoSlugPullrequestsPullRequestIdCommentsGet({
|
|
53273
|
+
workspace: repoContext.workspace,
|
|
53274
|
+
repoSlug: repoContext.repoSlug,
|
|
53275
|
+
pullRequestId: prId
|
|
53276
|
+
}, {
|
|
53277
|
+
params: { page, pagelen }
|
|
53278
|
+
});
|
|
53279
|
+
return response.data;
|
|
53280
|
+
}
|
|
53062
53281
|
});
|
|
53063
|
-
const data = response.data;
|
|
53064
|
-
const values = data.values ? Array.from(data.values) : [];
|
|
53065
53282
|
if (context.globalOptions.json) {
|
|
53066
53283
|
this.output.json({
|
|
53067
53284
|
pullRequestId: prId,
|
|
@@ -53463,10 +53680,18 @@ class GetConfigCommand extends BaseCommand {
|
|
|
53463
53680
|
async execute(options, context) {
|
|
53464
53681
|
const { key } = options;
|
|
53465
53682
|
if (GetConfigCommand.HIDDEN_KEYS.includes(key)) {
|
|
53466
|
-
throw new
|
|
53683
|
+
throw new BBError({
|
|
53684
|
+
code: 4003 /* CONFIG_INVALID_KEY */,
|
|
53685
|
+
message: `Cannot display '${key}' - use 'bb auth token' to get authentication credentials`,
|
|
53686
|
+
context: { key }
|
|
53687
|
+
});
|
|
53467
53688
|
}
|
|
53468
53689
|
if (!isReadableConfigKey(key)) {
|
|
53469
|
-
throw new
|
|
53690
|
+
throw new BBError({
|
|
53691
|
+
code: 4003 /* CONFIG_INVALID_KEY */,
|
|
53692
|
+
message: `Unknown config key '${key}'. Valid keys: ${READABLE_CONFIG_KEYS.join(", ")}`,
|
|
53693
|
+
context: { key }
|
|
53694
|
+
});
|
|
53470
53695
|
}
|
|
53471
53696
|
const rawValue = await this.configService.getValue(key);
|
|
53472
53697
|
const value = normalizeReadableConfigValue(key, rawValue);
|
|
@@ -53494,10 +53719,18 @@ class SetConfigCommand extends BaseCommand {
|
|
|
53494
53719
|
async execute(options, context) {
|
|
53495
53720
|
const { key, value } = options;
|
|
53496
53721
|
if (SetConfigCommand.PROTECTED_KEYS.includes(key)) {
|
|
53497
|
-
throw new
|
|
53722
|
+
throw new BBError({
|
|
53723
|
+
code: 4003 /* CONFIG_INVALID_KEY */,
|
|
53724
|
+
message: `Cannot set '${key}' directly. Use 'bb auth login' to configure authentication.`,
|
|
53725
|
+
context: { key }
|
|
53726
|
+
});
|
|
53498
53727
|
}
|
|
53499
53728
|
if (!isSettableConfigKey(key)) {
|
|
53500
|
-
throw new
|
|
53729
|
+
throw new BBError({
|
|
53730
|
+
code: 4003 /* CONFIG_INVALID_KEY */,
|
|
53731
|
+
message: `Unknown config key '${key}'. Valid keys: ${SETTABLE_CONFIG_KEYS.join(", ")}`,
|
|
53732
|
+
context: { key }
|
|
53733
|
+
});
|
|
53501
53734
|
}
|
|
53502
53735
|
const parsedValue = parseSettableConfigValue(key, value);
|
|
53503
53736
|
await this.configService.setValue(key, parsedValue);
|
|
@@ -53591,7 +53824,11 @@ class InstallCompletionCommand extends BaseCommand {
|
|
|
53591
53824
|
this.output.success("Shell completions installed successfully!");
|
|
53592
53825
|
this.output.text("Restart your shell or source your profile to enable completions.");
|
|
53593
53826
|
} catch (error) {
|
|
53594
|
-
throw new
|
|
53827
|
+
throw new BBError({
|
|
53828
|
+
code: 9999 /* UNKNOWN */,
|
|
53829
|
+
message: `Failed to install completions: ${error}`,
|
|
53830
|
+
cause: error instanceof Error ? error : undefined
|
|
53831
|
+
});
|
|
53595
53832
|
}
|
|
53596
53833
|
}
|
|
53597
53834
|
}
|
|
@@ -53622,7 +53859,11 @@ class UninstallCompletionCommand extends BaseCommand {
|
|
|
53622
53859
|
}
|
|
53623
53860
|
this.output.success("Shell completions uninstalled successfully!");
|
|
53624
53861
|
} catch (error) {
|
|
53625
|
-
throw new
|
|
53862
|
+
throw new BBError({
|
|
53863
|
+
code: 9999 /* UNKNOWN */,
|
|
53864
|
+
message: `Failed to uninstall completions: ${error}`,
|
|
53865
|
+
cause: error instanceof Error ? error : undefined
|
|
53866
|
+
});
|
|
53626
53867
|
}
|
|
53627
53868
|
}
|
|
53628
53869
|
}
|
|
@@ -54041,7 +54282,7 @@ prCmd.command("checkout <id>").description("Checkout a pull request locally").ac
|
|
|
54041
54282
|
const context = createContext(cli);
|
|
54042
54283
|
await runCommand(ServiceTokens.CheckoutPRCommand, withGlobalOptions({ id, ...options }, context), cli, context);
|
|
54043
54284
|
});
|
|
54044
|
-
prCmd.command("diff [id]").description("View pull request diff").option("--color <when>", "Colorize output (auto, always, never)", "auto").option("--name-only", "Show only names of changed files").option("--stat", "Show diffstat").option("
|
|
54285
|
+
prCmd.command("diff [id]").description("View pull request diff").option("--color <when>", "Colorize output (auto, always, never)", "auto").option("--name-only", "Show only names of changed files").option("--stat", "Show diffstat").option("--web", "Open diff in web browser").action(async (id, options) => {
|
|
54045
54286
|
const context = createContext(cli);
|
|
54046
54287
|
await runCommand(ServiceTokens.DiffPRCommand, withGlobalOptions({ id, ...options }, context), cli, context);
|
|
54047
54288
|
});
|