@pilatos/bitbucket-cli 0.3.2 → 1.1.0
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 +53 -231
- package/dist/index.js +450 -70
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,56 +32,20 @@
|
|
|
32
32
|
|
|
33
33
|
If you've used GitHub's `gh` CLI and loved it, you've probably wished for something similar for Bitbucket. **Now you have it.**
|
|
34
34
|
|
|
35
|
-
`bb` brings the power of command-line workflows to Bitbucket Cloud, letting you:
|
|
36
|
-
|
|
37
35
|
- **Stay in your terminal** — No more context-switching to the browser
|
|
38
|
-
- **Automate your workflow** — Script common operations with
|
|
36
|
+
- **Automate your workflow** — Script common operations with JSON output
|
|
39
37
|
- **Work faster** — Clone repos, create PRs, and manage code reviews in seconds
|
|
40
|
-
- **
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Table of Contents
|
|
45
|
-
|
|
46
|
-
- [Installation](#installation)
|
|
47
|
-
- [Quick Start](#quick-start)
|
|
48
|
-
- [Commands](#commands)
|
|
49
|
-
- [Authentication](#authentication)
|
|
50
|
-
- [Repositories](#repositories)
|
|
51
|
-
- [Pull Requests](#pull-requests)
|
|
52
|
-
- [Configuration](#configuration)
|
|
53
|
-
- [Shell Completion](#shell-completion)
|
|
54
|
-
- [Global Options](#global-options)
|
|
55
|
-
- [Authentication Setup](#authentication-setup)
|
|
56
|
-
- [Configuration](#configuration-1)
|
|
57
|
-
- [Examples](#examples)
|
|
58
|
-
- [Development](#development)
|
|
59
|
-
- [Contributing](#contributing)
|
|
60
|
-
- [License](#license)
|
|
38
|
+
- **Smart context detection** — Automatically detects workspace/repo from git directory
|
|
61
39
|
|
|
62
40
|
---
|
|
63
41
|
|
|
64
42
|
## Installation
|
|
65
43
|
|
|
66
|
-
### Using npm (Recommended)
|
|
67
|
-
|
|
68
44
|
```bash
|
|
69
45
|
npm install -g @pilatos/bitbucket-cli
|
|
70
46
|
```
|
|
71
47
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
bun install -g @pilatos/bitbucket-cli
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Using Yarn
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
yarn global add @pilatos/bitbucket-cli
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Verify Installation
|
|
48
|
+
Verify installation:
|
|
85
49
|
|
|
86
50
|
```bash
|
|
87
51
|
bb --version
|
|
@@ -93,21 +57,19 @@ bb --version
|
|
|
93
57
|
|
|
94
58
|
## Quick Start
|
|
95
59
|
|
|
96
|
-
Get up and running in under a minute:
|
|
97
|
-
|
|
98
60
|
```bash
|
|
99
61
|
# 1. Authenticate with Bitbucket
|
|
100
62
|
bb auth login
|
|
101
63
|
|
|
102
64
|
# 2. Clone a repository
|
|
103
65
|
bb repo clone myworkspace/myrepo
|
|
66
|
+
cd myrepo
|
|
104
67
|
|
|
105
68
|
# 3. Create a feature branch and make changes
|
|
106
|
-
cd myrepo
|
|
107
69
|
git checkout -b feature/awesome-feature
|
|
108
70
|
|
|
109
71
|
# 4. Create a pull request
|
|
110
|
-
bb pr create --title "Add awesome feature"
|
|
72
|
+
bb pr create --title "Add awesome feature"
|
|
111
73
|
|
|
112
74
|
# 5. List open pull requests
|
|
113
75
|
bb pr list
|
|
@@ -115,152 +77,53 @@ bb pr list
|
|
|
115
77
|
|
|
116
78
|
---
|
|
117
79
|
|
|
118
|
-
##
|
|
119
|
-
|
|
120
|
-
### Authentication
|
|
121
|
-
|
|
122
|
-
Manage your Bitbucket authentication securely.
|
|
123
|
-
|
|
124
|
-
| Command | Description |
|
|
125
|
-
|---------|-------------|
|
|
126
|
-
| `bb auth login` | Authenticate with Bitbucket using an App Password |
|
|
127
|
-
| `bb auth logout` | Log out and remove stored credentials |
|
|
128
|
-
| `bb auth status` | Check your current authentication status |
|
|
129
|
-
| `bb auth token` | Print your current access token |
|
|
80
|
+
## Features
|
|
130
81
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
|
136
|
-
|
|
137
|
-
|
|
|
138
|
-
| `bb repo create <name>` | Create a new repository |
|
|
139
|
-
| `bb repo list` | List repositories in a workspace |
|
|
140
|
-
| `bb repo view [repo]` | View repository details and metadata |
|
|
141
|
-
| `bb repo delete <repo>` | Delete a repository (use with caution!) |
|
|
142
|
-
|
|
143
|
-
### Pull Requests
|
|
144
|
-
|
|
145
|
-
Full pull request workflow management from your terminal.
|
|
146
|
-
|
|
147
|
-
| Command | Description |
|
|
148
|
-
|---------|-------------|
|
|
149
|
-
| `bb pr create` | Create a new pull request |
|
|
150
|
-
| `bb pr list` | List pull requests with filtering options |
|
|
151
|
-
| `bb pr view <id>` | View pull request details, diff, and comments |
|
|
152
|
-
| `bb pr merge <id>` | Merge a pull request |
|
|
153
|
-
| `bb pr approve <id>` | Approve a pull request |
|
|
154
|
-
| `bb pr decline <id>` | Decline a pull request |
|
|
155
|
-
| `bb pr checkout <id>` | Checkout a pull request branch locally |
|
|
156
|
-
|
|
157
|
-
### Configuration
|
|
158
|
-
|
|
159
|
-
Customize your CLI experience.
|
|
160
|
-
|
|
161
|
-
| Command | Description |
|
|
162
|
-
|---------|-------------|
|
|
163
|
-
| `bb config get <key>` | Get a configuration value |
|
|
164
|
-
| `bb config set <key> <value>` | Set a configuration value |
|
|
165
|
-
| `bb config list` | List all configuration values |
|
|
166
|
-
|
|
167
|
-
### Shell Completion
|
|
168
|
-
|
|
169
|
-
Enable intelligent tab completion for faster command entry.
|
|
170
|
-
|
|
171
|
-
| Command | Description |
|
|
172
|
-
|---------|-------------|
|
|
173
|
-
| `bb completion install` | Install shell completions (auto-detects shell) |
|
|
174
|
-
| `bb completion uninstall` | Remove shell completions |
|
|
175
|
-
|
|
176
|
-
**Setup:**
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
# Install completions
|
|
180
|
-
bb completion install
|
|
181
|
-
|
|
182
|
-
# Restart your shell or source your profile
|
|
183
|
-
source ~/.bashrc # Bash
|
|
184
|
-
source ~/.zshrc # Zsh
|
|
185
|
-
source ~/.config/fish/config.fish # Fish
|
|
186
|
-
```
|
|
82
|
+
| Category | Commands |
|
|
83
|
+
|----------|----------|
|
|
84
|
+
| **Authentication** | `login`, `logout`, `status`, `token` |
|
|
85
|
+
| **Repositories** | `clone`, `create`, `list`, `view`, `delete` |
|
|
86
|
+
| **Pull Requests** | `create`, `list`, `view`, `edit`, `merge`, `approve`, `decline`, `checkout`, `diff` |
|
|
87
|
+
| **Configuration** | `get`, `set`, `list` |
|
|
88
|
+
| **Shell Completion** | `install`, `uninstall` |
|
|
187
89
|
|
|
188
|
-
**
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
bb repo cl<Tab> # → bb repo clone
|
|
193
|
-
bb pr l<Tab> # → bb pr list
|
|
194
|
-
```
|
|
90
|
+
**Global Options:**
|
|
91
|
+
- `--json` — Output results as JSON for scripting
|
|
92
|
+
- `-w, --workspace` — Specify workspace
|
|
93
|
+
- `-r, --repo` — Specify repository
|
|
195
94
|
|
|
196
95
|
---
|
|
197
96
|
|
|
198
|
-
##
|
|
97
|
+
## Documentation
|
|
199
98
|
|
|
200
|
-
|
|
99
|
+
Full documentation is available at **[bitbucket-cli.paulvanderlei.com](https://bitbucket-cli.paulvanderlei.com)**
|
|
201
100
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
| `-v, --version` | Show version number |
|
|
101
|
+
- [Quick Start Guide](https://bitbucket-cli.paulvanderlei.com/getting-started/quickstart/)
|
|
102
|
+
- [Command Reference](https://bitbucket-cli.paulvanderlei.com/commands/auth/)
|
|
103
|
+
- [Scripting & Automation](https://bitbucket-cli.paulvanderlei.com/guides/scripting/)
|
|
104
|
+
- [CI/CD Integration](https://bitbucket-cli.paulvanderlei.com/guides/cicd/)
|
|
105
|
+
- [Troubleshooting](https://bitbucket-cli.paulvanderlei.com/help/troubleshooting/)
|
|
106
|
+
- [FAQ](https://bitbucket-cli.paulvanderlei.com/help/faq/)
|
|
209
107
|
|
|
210
108
|
---
|
|
211
109
|
|
|
212
|
-
## Authentication
|
|
213
|
-
|
|
214
|
-
The CLI uses **Bitbucket App Passwords** for secure authentication. Here's how to set it up:
|
|
215
|
-
|
|
216
|
-
### Step 1: Create an App Password
|
|
217
|
-
|
|
218
|
-
1. Go to [Bitbucket App Passwords](https://bitbucket.org/account/settings/app-passwords/)
|
|
219
|
-
2. Click **"Create app password"**
|
|
220
|
-
3. Enter a descriptive label (e.g., "bb CLI")
|
|
221
|
-
4. Select the required permissions:
|
|
222
|
-
- **Account:** Read
|
|
223
|
-
- **Repositories:** Read, Write, Admin (as needed)
|
|
224
|
-
- **Pull requests:** Read, Write
|
|
225
|
-
5. Click **"Create"**
|
|
226
|
-
6. **Copy the generated password** (you won't see it again!)
|
|
227
|
-
|
|
228
|
-
### Step 2: Authenticate
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
bb auth login
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Enter your Bitbucket username and the App Password when prompted.
|
|
110
|
+
## Authentication
|
|
235
111
|
|
|
236
|
-
|
|
112
|
+
The CLI uses **Bitbucket API Tokens** for secure authentication.
|
|
237
113
|
|
|
238
|
-
|
|
239
|
-
bb auth status
|
|
240
|
-
```
|
|
114
|
+
> **Note**: As of September 9, 2025, Bitbucket app passwords are deprecated. Use API tokens instead.
|
|
241
115
|
|
|
242
|
-
|
|
116
|
+
### Create an API Token
|
|
243
117
|
|
|
244
|
-
|
|
118
|
+
1. Go to [Bitbucket API Tokens](https://bitbucket.org/account/settings/api-tokens/)
|
|
119
|
+
2. Click **"Create API token"**
|
|
120
|
+
3. Select required scopes (Account, Repositories, Pull requests)
|
|
121
|
+
4. Copy the token
|
|
245
122
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
| Platform | Location |
|
|
249
|
-
|----------|----------|
|
|
250
|
-
| **macOS / Linux** | `~/.config/bb/config.json` |
|
|
251
|
-
| **Windows** | `%APPDATA%\bb\config.json` |
|
|
252
|
-
|
|
253
|
-
### Available Settings
|
|
123
|
+
### Authenticate
|
|
254
124
|
|
|
255
125
|
```bash
|
|
256
|
-
|
|
257
|
-
bb config set workspace myworkspace
|
|
258
|
-
|
|
259
|
-
# Set your default repository
|
|
260
|
-
bb config set repo myrepo
|
|
261
|
-
|
|
262
|
-
# View all settings
|
|
263
|
-
bb config list
|
|
126
|
+
bb auth login
|
|
264
127
|
```
|
|
265
128
|
|
|
266
129
|
---
|
|
@@ -270,53 +133,38 @@ bb config list
|
|
|
270
133
|
### Daily Workflow
|
|
271
134
|
|
|
272
135
|
```bash
|
|
273
|
-
#
|
|
274
|
-
bb pr list
|
|
136
|
+
# Check open PRs
|
|
137
|
+
bb pr list
|
|
275
138
|
|
|
276
|
-
# Review
|
|
139
|
+
# Review and merge
|
|
277
140
|
bb pr view 42
|
|
278
|
-
|
|
279
|
-
# Approve and merge
|
|
280
141
|
bb pr approve 42
|
|
281
142
|
bb pr merge 42
|
|
282
143
|
```
|
|
283
144
|
|
|
284
|
-
###
|
|
145
|
+
### Scripting with JSON
|
|
285
146
|
|
|
286
147
|
```bash
|
|
287
|
-
#
|
|
288
|
-
bb pr
|
|
289
|
-
--title "feat: Add user notifications" \
|
|
290
|
-
--description "Implements real-time notifications using WebSockets" \
|
|
291
|
-
--source feature/notifications \
|
|
292
|
-
--destination main
|
|
293
|
-
```
|
|
148
|
+
# Get all open PR titles
|
|
149
|
+
bb pr list --json | jq '.[].title'
|
|
294
150
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
```bash
|
|
298
|
-
# Get all open PRs as JSON for processing
|
|
299
|
-
bb pr list --state OPEN --json | jq '.[] | .title'
|
|
300
|
-
|
|
301
|
-
# List repos and filter by name
|
|
151
|
+
# Filter repos by name
|
|
302
152
|
bb repo list --json | jq '.[] | select(.name | contains("api"))'
|
|
303
153
|
```
|
|
304
154
|
|
|
305
|
-
###
|
|
155
|
+
### CI/CD Usage
|
|
306
156
|
|
|
307
157
|
```bash
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
bb
|
|
311
|
-
|
|
158
|
+
export BB_USERNAME=myuser
|
|
159
|
+
export BB_API_TOKEN=my-token
|
|
160
|
+
bb auth login
|
|
161
|
+
bb pr list -w workspace -r repo --json
|
|
312
162
|
```
|
|
313
163
|
|
|
314
164
|
---
|
|
315
165
|
|
|
316
166
|
## Development
|
|
317
167
|
|
|
318
|
-
Want to contribute or run locally? Here's how:
|
|
319
|
-
|
|
320
168
|
```bash
|
|
321
169
|
# Clone the repository
|
|
322
170
|
git clone https://github.com/0pilatos0/bitbucket-cli.git
|
|
@@ -333,59 +181,33 @@ bun test
|
|
|
333
181
|
|
|
334
182
|
# Build for production
|
|
335
183
|
bun run build
|
|
336
|
-
|
|
337
|
-
# Generate API client from OpenAPI spec
|
|
338
|
-
bun run generate:api
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### Project Structure
|
|
342
|
-
|
|
343
|
-
```
|
|
344
|
-
bitbucket-cli/
|
|
345
|
-
├── src/
|
|
346
|
-
│ ├── commands/ # Command implementations
|
|
347
|
-
│ ├── core/ # Core utilities and base classes
|
|
348
|
-
│ └── index.ts # Entry point
|
|
349
|
-
├── tests/ # Test files
|
|
350
|
-
├── docs/ # Documentation site (Astro)
|
|
351
|
-
└── specs/ # OpenAPI specifications
|
|
352
184
|
```
|
|
353
185
|
|
|
354
186
|
---
|
|
355
187
|
|
|
356
188
|
## Contributing
|
|
357
189
|
|
|
358
|
-
We welcome contributions
|
|
359
|
-
|
|
360
|
-
- Reporting bugs
|
|
361
|
-
- Suggesting new features
|
|
362
|
-
- Improving documentation
|
|
363
|
-
- Submitting pull requests
|
|
364
|
-
|
|
365
|
-
Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
366
|
-
|
|
367
|
-
### Quick Contribution Steps
|
|
190
|
+
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
368
191
|
|
|
369
192
|
1. Fork the repository
|
|
370
|
-
2. Create a feature branch
|
|
193
|
+
2. Create a feature branch
|
|
371
194
|
3. Make your changes
|
|
372
195
|
4. Run tests (`bun test`)
|
|
373
|
-
5.
|
|
374
|
-
6. Push and open a Pull Request
|
|
196
|
+
5. Submit a Pull Request
|
|
375
197
|
|
|
376
198
|
---
|
|
377
199
|
|
|
378
200
|
## Acknowledgments
|
|
379
201
|
|
|
380
|
-
- Inspired by [GitHub CLI (`gh`)](https://cli.github.com/)
|
|
381
|
-
- Built with [Commander.js](https://github.com/tj/commander.js)
|
|
202
|
+
- Inspired by [GitHub CLI (`gh`)](https://cli.github.com/)
|
|
203
|
+
- Built with [Commander.js](https://github.com/tj/commander.js)
|
|
382
204
|
- Uses the [Bitbucket Cloud REST API](https://developer.atlassian.com/cloud/bitbucket/rest/)
|
|
383
205
|
|
|
384
206
|
---
|
|
385
207
|
|
|
386
208
|
## License
|
|
387
209
|
|
|
388
|
-
|
|
210
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
389
211
|
|
|
390
212
|
---
|
|
391
213
|
|
package/dist/index.js
CHANGED
|
@@ -27629,7 +27629,7 @@ var require_iso2022 = __commonJS((exports, module) => {
|
|
|
27629
27629
|
|
|
27630
27630
|
// node_modules/external-editor/node_modules/chardet/index.js
|
|
27631
27631
|
var require_chardet = __commonJS((exports, module) => {
|
|
27632
|
-
var
|
|
27632
|
+
var fs2 = __require("fs");
|
|
27633
27633
|
var utf8 = require_utf8();
|
|
27634
27634
|
var unicode = require_unicode();
|
|
27635
27635
|
var mbcs = require_mbcs();
|
|
@@ -27703,29 +27703,29 @@ var require_chardet = __commonJS((exports, module) => {
|
|
|
27703
27703
|
var fd;
|
|
27704
27704
|
var handler = function(err, buffer) {
|
|
27705
27705
|
if (fd) {
|
|
27706
|
-
|
|
27706
|
+
fs2.closeSync(fd);
|
|
27707
27707
|
}
|
|
27708
27708
|
if (err)
|
|
27709
27709
|
return cb(err, null);
|
|
27710
27710
|
cb(null, self2.detect(buffer, opts));
|
|
27711
27711
|
};
|
|
27712
27712
|
if (opts && opts.sampleSize) {
|
|
27713
|
-
fd =
|
|
27714
|
-
|
|
27713
|
+
fd = fs2.openSync(filepath, "r"), sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
27714
|
+
fs2.read(fd, sample, 0, opts.sampleSize, null, function(err) {
|
|
27715
27715
|
handler(err, sample);
|
|
27716
27716
|
});
|
|
27717
27717
|
return;
|
|
27718
27718
|
}
|
|
27719
|
-
|
|
27719
|
+
fs2.readFile(filepath, handler);
|
|
27720
27720
|
};
|
|
27721
27721
|
module.exports.detectFileSync = function(filepath, opts) {
|
|
27722
27722
|
if (opts && opts.sampleSize) {
|
|
27723
|
-
var fd =
|
|
27724
|
-
|
|
27725
|
-
|
|
27723
|
+
var fd = fs2.openSync(filepath, "r"), sample2 = Buffer.allocUnsafe(opts.sampleSize);
|
|
27724
|
+
fs2.readSync(fd, sample2, 0, opts.sampleSize);
|
|
27725
|
+
fs2.closeSync(fd);
|
|
27726
27726
|
return self2.detect(sample2, opts);
|
|
27727
27727
|
}
|
|
27728
|
-
return self2.detect(
|
|
27728
|
+
return self2.detect(fs2.readFileSync(filepath), opts);
|
|
27729
27729
|
};
|
|
27730
27730
|
module.exports.detectAll = function(buffer, opts) {
|
|
27731
27731
|
if (typeof opts !== "object") {
|
|
@@ -31087,7 +31087,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31087
31087
|
*
|
|
31088
31088
|
* MIT Licensed
|
|
31089
31089
|
*/
|
|
31090
|
-
var
|
|
31090
|
+
var fs2 = __require("fs");
|
|
31091
31091
|
var path = __require("path");
|
|
31092
31092
|
var crypto = __require("crypto");
|
|
31093
31093
|
var osTmpDir = require_os_tmpdir();
|
|
@@ -31151,7 +31151,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31151
31151
|
return cb(new Error("Invalid template provided"));
|
|
31152
31152
|
(function _getUniqueName() {
|
|
31153
31153
|
const name = _generateTmpName(opts);
|
|
31154
|
-
|
|
31154
|
+
fs2.stat(name, function(err) {
|
|
31155
31155
|
if (!err) {
|
|
31156
31156
|
if (tries-- > 0)
|
|
31157
31157
|
return _getUniqueName();
|
|
@@ -31170,7 +31170,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31170
31170
|
do {
|
|
31171
31171
|
const name = _generateTmpName(opts);
|
|
31172
31172
|
try {
|
|
31173
|
-
|
|
31173
|
+
fs2.statSync(name);
|
|
31174
31174
|
} catch (e) {
|
|
31175
31175
|
return name;
|
|
31176
31176
|
}
|
|
@@ -31183,14 +31183,14 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31183
31183
|
tmpName(opts, function _tmpNameCreated(err, name) {
|
|
31184
31184
|
if (err)
|
|
31185
31185
|
return cb(err);
|
|
31186
|
-
|
|
31186
|
+
fs2.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err2, fd) {
|
|
31187
31187
|
if (err2)
|
|
31188
31188
|
return cb(err2);
|
|
31189
31189
|
if (opts.discardDescriptor) {
|
|
31190
|
-
return
|
|
31190
|
+
return fs2.close(fd, function _discardCallback(err3) {
|
|
31191
31191
|
if (err3) {
|
|
31192
31192
|
try {
|
|
31193
|
-
|
|
31193
|
+
fs2.unlinkSync(name);
|
|
31194
31194
|
} catch (e) {
|
|
31195
31195
|
if (!isENOENT(e)) {
|
|
31196
31196
|
err3 = e;
|
|
@@ -31213,9 +31213,9 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31213
31213
|
opts.postfix = opts.postfix || ".tmp";
|
|
31214
31214
|
const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
|
|
31215
31215
|
const name = tmpNameSync(opts);
|
|
31216
|
-
var fd =
|
|
31216
|
+
var fd = fs2.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);
|
|
31217
31217
|
if (opts.discardDescriptor) {
|
|
31218
|
-
|
|
31218
|
+
fs2.closeSync(fd);
|
|
31219
31219
|
fd = undefined;
|
|
31220
31220
|
}
|
|
31221
31221
|
return {
|
|
@@ -31227,9 +31227,9 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31227
31227
|
function _rmdirRecursiveSync(root) {
|
|
31228
31228
|
const dirs = [root];
|
|
31229
31229
|
do {
|
|
31230
|
-
var dir2 = dirs.pop(), deferred = false, files =
|
|
31230
|
+
var dir2 = dirs.pop(), deferred = false, files = fs2.readdirSync(dir2);
|
|
31231
31231
|
for (var i = 0, length = files.length;i < length; i++) {
|
|
31232
|
-
var file2 = path.join(dir2, files[i]), stat =
|
|
31232
|
+
var file2 = path.join(dir2, files[i]), stat = fs2.lstatSync(file2);
|
|
31233
31233
|
if (stat.isDirectory()) {
|
|
31234
31234
|
if (!deferred) {
|
|
31235
31235
|
deferred = true;
|
|
@@ -31237,11 +31237,11 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31237
31237
|
}
|
|
31238
31238
|
dirs.push(file2);
|
|
31239
31239
|
} else {
|
|
31240
|
-
|
|
31240
|
+
fs2.unlinkSync(file2);
|
|
31241
31241
|
}
|
|
31242
31242
|
}
|
|
31243
31243
|
if (!deferred) {
|
|
31244
|
-
|
|
31244
|
+
fs2.rmdirSync(dir2);
|
|
31245
31245
|
}
|
|
31246
31246
|
} while (dirs.length !== 0);
|
|
31247
31247
|
}
|
|
@@ -31250,7 +31250,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31250
31250
|
tmpName(opts, function _tmpNameCreated(err, name) {
|
|
31251
31251
|
if (err)
|
|
31252
31252
|
return cb(err);
|
|
31253
|
-
|
|
31253
|
+
fs2.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err2) {
|
|
31254
31254
|
if (err2)
|
|
31255
31255
|
return cb(err2);
|
|
31256
31256
|
cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
|
|
@@ -31260,7 +31260,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31260
31260
|
function dirSync(options) {
|
|
31261
31261
|
var args = _parseArguments(options), opts = args[0];
|
|
31262
31262
|
const name = tmpNameSync(opts);
|
|
31263
|
-
|
|
31263
|
+
fs2.mkdirSync(name, opts.mode || DIR_MODE);
|
|
31264
31264
|
return {
|
|
31265
31265
|
name,
|
|
31266
31266
|
removeCallback: _prepareTmpDirRemoveCallback(name, opts)
|
|
@@ -31270,7 +31270,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31270
31270
|
const removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
|
|
31271
31271
|
try {
|
|
31272
31272
|
if (0 <= fdPath[0]) {
|
|
31273
|
-
|
|
31273
|
+
fs2.closeSync(fdPath[0]);
|
|
31274
31274
|
}
|
|
31275
31275
|
} catch (e) {
|
|
31276
31276
|
if (!isEBADF(e) && !isENOENT(e)) {
|
|
@@ -31278,7 +31278,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31278
31278
|
}
|
|
31279
31279
|
}
|
|
31280
31280
|
try {
|
|
31281
|
-
|
|
31281
|
+
fs2.unlinkSync(fdPath[1]);
|
|
31282
31282
|
} catch (e) {
|
|
31283
31283
|
if (!isENOENT(e)) {
|
|
31284
31284
|
throw e;
|
|
@@ -31291,7 +31291,7 @@ var require_tmp = __commonJS((exports, module) => {
|
|
|
31291
31291
|
return removeCallback;
|
|
31292
31292
|
}
|
|
31293
31293
|
function _prepareTmpDirRemoveCallback(name, opts) {
|
|
31294
|
-
const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync :
|
|
31294
|
+
const removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs2.rmdirSync.bind(fs2);
|
|
31295
31295
|
const removeCallback = _prepareRemoveCallback(removeFunction, name);
|
|
31296
31296
|
if (!opts.keep) {
|
|
31297
31297
|
_removeObjects.unshift(removeCallback);
|
|
@@ -32550,13 +32550,13 @@ var require_src = __commonJS((exports, module) => {
|
|
|
32550
32550
|
|
|
32551
32551
|
// node_modules/tabtab/lib/utils/tabtabDebug.js
|
|
32552
32552
|
var require_tabtabDebug = __commonJS((exports, module) => {
|
|
32553
|
-
var
|
|
32553
|
+
var fs2 = __require("fs");
|
|
32554
32554
|
var util = __require("util");
|
|
32555
32555
|
var tabtabDebug = (name) => {
|
|
32556
32556
|
let debug = require_src()(name);
|
|
32557
32557
|
if (process.env.TABTAB_DEBUG) {
|
|
32558
32558
|
const file = process.env.TABTAB_DEBUG;
|
|
32559
|
-
const stream =
|
|
32559
|
+
const stream = fs2.createWriteStream(file, {
|
|
32560
32560
|
flags: "a+"
|
|
32561
32561
|
});
|
|
32562
32562
|
const log = (...args) => {
|
|
@@ -32653,14 +32653,14 @@ var require_promisify = __commonJS((exports) => {
|
|
|
32653
32653
|
Object.defineProperty(exports, "__esModule", {
|
|
32654
32654
|
value: true
|
|
32655
32655
|
});
|
|
32656
|
-
exports.promisify =
|
|
32656
|
+
exports.promisify = promisify2;
|
|
32657
32657
|
var customArgumentsToken = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";
|
|
32658
|
-
function
|
|
32658
|
+
function promisify2(original) {
|
|
32659
32659
|
if (typeof original !== "function") {
|
|
32660
32660
|
throw new TypeError("Argument to promisify must be a function");
|
|
32661
32661
|
}
|
|
32662
32662
|
var argumentNames = original[customArgumentsToken];
|
|
32663
|
-
var ES6Promise =
|
|
32663
|
+
var ES6Promise = promisify2.Promise || Promise;
|
|
32664
32664
|
if (typeof ES6Promise !== "function") {
|
|
32665
32665
|
throw new Error("No Promise implementation found; do you need a polyfill?");
|
|
32666
32666
|
}
|
|
@@ -32693,14 +32693,14 @@ var require_promisify = __commonJS((exports) => {
|
|
|
32693
32693
|
});
|
|
32694
32694
|
};
|
|
32695
32695
|
}
|
|
32696
|
-
|
|
32697
|
-
|
|
32696
|
+
promisify2.argumentNames = customArgumentsToken;
|
|
32697
|
+
promisify2.Promise = undefined;
|
|
32698
32698
|
});
|
|
32699
32699
|
|
|
32700
32700
|
// node_modules/mkdirp/index.js
|
|
32701
32701
|
var require_mkdirp = __commonJS((exports, module) => {
|
|
32702
32702
|
var path = __require("path");
|
|
32703
|
-
var
|
|
32703
|
+
var fs2 = __require("fs");
|
|
32704
32704
|
var _0777 = parseInt("0777", 8);
|
|
32705
32705
|
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
|
|
32706
32706
|
function mkdirP(p, opts, f, made) {
|
|
@@ -32711,7 +32711,7 @@ var require_mkdirp = __commonJS((exports, module) => {
|
|
|
32711
32711
|
opts = { mode: opts };
|
|
32712
32712
|
}
|
|
32713
32713
|
var mode = opts.mode;
|
|
32714
|
-
var xfs = opts.fs ||
|
|
32714
|
+
var xfs = opts.fs || fs2;
|
|
32715
32715
|
if (mode === undefined) {
|
|
32716
32716
|
mode = _0777;
|
|
32717
32717
|
}
|
|
@@ -32751,7 +32751,7 @@ var require_mkdirp = __commonJS((exports, module) => {
|
|
|
32751
32751
|
opts = { mode: opts };
|
|
32752
32752
|
}
|
|
32753
32753
|
var mode = opts.mode;
|
|
32754
|
-
var xfs = opts.fs ||
|
|
32754
|
+
var xfs = opts.fs || fs2;
|
|
32755
32755
|
if (mode === undefined) {
|
|
32756
32756
|
mode = _0777;
|
|
32757
32757
|
}
|
|
@@ -32791,10 +32791,10 @@ var require_systemShell = __commonJS((exports, module) => {
|
|
|
32791
32791
|
|
|
32792
32792
|
// node_modules/tabtab/lib/utils/exists.js
|
|
32793
32793
|
var require_exists = __commonJS((exports, module) => {
|
|
32794
|
-
var
|
|
32794
|
+
var fs2 = __require("fs");
|
|
32795
32795
|
var untildify = require_untildify();
|
|
32796
|
-
var { promisify } = require_promisify();
|
|
32797
|
-
var readFile =
|
|
32796
|
+
var { promisify: promisify2 } = require_promisify();
|
|
32797
|
+
var readFile = promisify2(fs2.readFile);
|
|
32798
32798
|
module.exports = async (file) => {
|
|
32799
32799
|
let fileExists;
|
|
32800
32800
|
try {
|
|
@@ -32838,16 +32838,16 @@ var require_constants = __commonJS((exports, module) => {
|
|
|
32838
32838
|
// node_modules/tabtab/lib/installer.js
|
|
32839
32839
|
var require_installer = __commonJS((exports, module) => {
|
|
32840
32840
|
var __dirname = "/home/runner/work/bitbucket-cli/bitbucket-cli/node_modules/tabtab/lib";
|
|
32841
|
-
var
|
|
32841
|
+
var fs2 = __require("fs");
|
|
32842
32842
|
var path = __require("path");
|
|
32843
32843
|
var untildify = require_untildify();
|
|
32844
|
-
var { promisify } = require_promisify();
|
|
32845
|
-
var mkdirp =
|
|
32844
|
+
var { promisify: promisify2 } = require_promisify();
|
|
32845
|
+
var mkdirp = promisify2(require_mkdirp());
|
|
32846
32846
|
var { tabtabDebug, systemShell, exists } = require_utils2();
|
|
32847
32847
|
var debug = tabtabDebug("tabtab:installer");
|
|
32848
|
-
var readFile =
|
|
32849
|
-
var writeFile =
|
|
32850
|
-
var unlink =
|
|
32848
|
+
var readFile = promisify2(fs2.readFile);
|
|
32849
|
+
var writeFile = promisify2(fs2.writeFile);
|
|
32850
|
+
var unlink = promisify2(fs2.unlink);
|
|
32851
32851
|
var {
|
|
32852
32852
|
BASH_LOCATION,
|
|
32853
32853
|
FISH_LOCATION,
|
|
@@ -32907,7 +32907,7 @@ var require_installer = __commonJS((exports, module) => {
|
|
|
32907
32907
|
const filepath = untildify(filename);
|
|
32908
32908
|
debug("Creating directory for %s file", filepath);
|
|
32909
32909
|
mkdirp(path.dirname(filepath)).then(() => {
|
|
32910
|
-
const stream =
|
|
32910
|
+
const stream = fs2.createWriteStream(filepath, { flags: "a" });
|
|
32911
32911
|
stream.on("error", reject);
|
|
32912
32912
|
stream.on("finish", () => resolve());
|
|
32913
32913
|
debug("Writing to shell configuration file (%s)", filename);
|
|
@@ -33258,10 +33258,12 @@ var ServiceTokens = {
|
|
|
33258
33258
|
CreatePRCommand: "CreatePRCommand",
|
|
33259
33259
|
ListPRsCommand: "ListPRsCommand",
|
|
33260
33260
|
ViewPRCommand: "ViewPRCommand",
|
|
33261
|
+
EditPRCommand: "EditPRCommand",
|
|
33261
33262
|
MergePRCommand: "MergePRCommand",
|
|
33262
33263
|
ApprovePRCommand: "ApprovePRCommand",
|
|
33263
33264
|
DeclinePRCommand: "DeclinePRCommand",
|
|
33264
33265
|
CheckoutPRCommand: "CheckoutPRCommand",
|
|
33266
|
+
DiffPRCommand: "DiffPRCommand",
|
|
33265
33267
|
GetConfigCommand: "GetConfigCommand",
|
|
33266
33268
|
SetConfigCommand: "SetConfigCommand",
|
|
33267
33269
|
ListConfigCommand: "ListConfigCommand",
|
|
@@ -33465,14 +33467,14 @@ class ConfigService {
|
|
|
33465
33467
|
if (!configResult.success) {
|
|
33466
33468
|
return configResult;
|
|
33467
33469
|
}
|
|
33468
|
-
const { username,
|
|
33469
|
-
if (!username || !
|
|
33470
|
+
const { username, apiToken } = configResult.value;
|
|
33471
|
+
if (!username || !apiToken) {
|
|
33470
33472
|
return Result.err(new BBError({
|
|
33471
33473
|
code: 1001 /* AUTH_REQUIRED */,
|
|
33472
33474
|
message: "Authentication required. Run 'bb auth login' to authenticate."
|
|
33473
33475
|
}));
|
|
33474
33476
|
}
|
|
33475
|
-
return Result.ok({ username,
|
|
33477
|
+
return Result.ok({ username, apiToken });
|
|
33476
33478
|
}
|
|
33477
33479
|
async setCredentials(credentials) {
|
|
33478
33480
|
const configResult = await this.getConfig();
|
|
@@ -33482,7 +33484,7 @@ class ConfigService {
|
|
|
33482
33484
|
return this.setConfig({
|
|
33483
33485
|
...configResult.value,
|
|
33484
33486
|
username: credentials.username,
|
|
33485
|
-
|
|
33487
|
+
apiToken: credentials.apiToken
|
|
33486
33488
|
});
|
|
33487
33489
|
}
|
|
33488
33490
|
async clearConfig() {
|
|
@@ -34253,11 +34255,11 @@ class HttpClient {
|
|
|
34253
34255
|
if (!credentialsResult.success) {
|
|
34254
34256
|
return credentialsResult;
|
|
34255
34257
|
}
|
|
34256
|
-
const { username,
|
|
34257
|
-
const encoded = Buffer.from(`${username}:${
|
|
34258
|
+
const { username, apiToken } = credentialsResult.value;
|
|
34259
|
+
const encoded = Buffer.from(`${username}:${apiToken}`).toString("base64");
|
|
34258
34260
|
return Result.ok(`Basic ${encoded}`);
|
|
34259
34261
|
}
|
|
34260
|
-
async request(method, path, body) {
|
|
34262
|
+
async request(method, path, body, acceptText = false) {
|
|
34261
34263
|
const authResult = await this.getAuthHeader();
|
|
34262
34264
|
if (!authResult.success) {
|
|
34263
34265
|
return authResult;
|
|
@@ -34265,7 +34267,7 @@ class HttpClient {
|
|
|
34265
34267
|
const headers = {
|
|
34266
34268
|
Authorization: authResult.value,
|
|
34267
34269
|
"Content-Type": "application/json",
|
|
34268
|
-
Accept: "application/json"
|
|
34270
|
+
Accept: acceptText ? "text/plain" : "application/json"
|
|
34269
34271
|
};
|
|
34270
34272
|
const url = `${this.baseUrl}${path}`;
|
|
34271
34273
|
try {
|
|
@@ -34278,7 +34280,7 @@ class HttpClient {
|
|
|
34278
34280
|
signal: controller.signal
|
|
34279
34281
|
});
|
|
34280
34282
|
clearTimeout(timeoutId);
|
|
34281
|
-
return this.handleResponse(response);
|
|
34283
|
+
return acceptText ? this.handleTextResponse(response) : this.handleResponse(response);
|
|
34282
34284
|
} catch (error) {
|
|
34283
34285
|
if (error instanceof Error && error.name === "AbortError") {
|
|
34284
34286
|
return Result.err(new BBError({
|
|
@@ -34297,11 +34299,12 @@ class HttpClient {
|
|
|
34297
34299
|
}
|
|
34298
34300
|
async handleResponse(response) {
|
|
34299
34301
|
if (!response.ok) {
|
|
34302
|
+
const textBody = await response.text();
|
|
34300
34303
|
let errorBody;
|
|
34301
34304
|
try {
|
|
34302
|
-
errorBody =
|
|
34305
|
+
errorBody = JSON.parse(textBody);
|
|
34303
34306
|
} catch {
|
|
34304
|
-
errorBody =
|
|
34307
|
+
errorBody = textBody;
|
|
34305
34308
|
}
|
|
34306
34309
|
const message = this.extractErrorMessage(errorBody, response.statusText);
|
|
34307
34310
|
return Result.err(new APIError(message, response.status, errorBody, {
|
|
@@ -34322,6 +34325,34 @@ class HttpClient {
|
|
|
34322
34325
|
}));
|
|
34323
34326
|
}
|
|
34324
34327
|
}
|
|
34328
|
+
async handleTextResponse(response) {
|
|
34329
|
+
if (!response.ok) {
|
|
34330
|
+
const textBody = await response.text();
|
|
34331
|
+
let errorBody;
|
|
34332
|
+
try {
|
|
34333
|
+
errorBody = JSON.parse(textBody);
|
|
34334
|
+
} catch {
|
|
34335
|
+
errorBody = textBody;
|
|
34336
|
+
}
|
|
34337
|
+
const message = this.extractErrorMessage(errorBody, response.statusText);
|
|
34338
|
+
return Result.err(new APIError(message, response.status, errorBody, {
|
|
34339
|
+
url: response.url
|
|
34340
|
+
}));
|
|
34341
|
+
}
|
|
34342
|
+
if (response.status === 204) {
|
|
34343
|
+
return Result.ok(undefined);
|
|
34344
|
+
}
|
|
34345
|
+
try {
|
|
34346
|
+
const data = await response.text();
|
|
34347
|
+
return Result.ok(data);
|
|
34348
|
+
} catch (error) {
|
|
34349
|
+
return Result.err(new BBError({
|
|
34350
|
+
code: 2001 /* API_REQUEST_FAILED */,
|
|
34351
|
+
message: "Failed to read response text",
|
|
34352
|
+
cause: error instanceof Error ? error : undefined
|
|
34353
|
+
}));
|
|
34354
|
+
}
|
|
34355
|
+
}
|
|
34325
34356
|
extractErrorMessage(body, fallback) {
|
|
34326
34357
|
if (typeof body === "object" && body !== null) {
|
|
34327
34358
|
const obj = body;
|
|
@@ -34340,6 +34371,9 @@ class HttpClient {
|
|
|
34340
34371
|
async get(path) {
|
|
34341
34372
|
return this.request("GET", path);
|
|
34342
34373
|
}
|
|
34374
|
+
async getText(path) {
|
|
34375
|
+
return this.request("GET", path, undefined, true);
|
|
34376
|
+
}
|
|
34343
34377
|
async post(path, body) {
|
|
34344
34378
|
return this.request("POST", path, body);
|
|
34345
34379
|
}
|
|
@@ -34399,6 +34433,9 @@ class PullRequestRepository {
|
|
|
34399
34433
|
async create(workspace, repoSlug, request) {
|
|
34400
34434
|
return this.httpClient.post(this.buildPath(workspace, repoSlug), request);
|
|
34401
34435
|
}
|
|
34436
|
+
async update(workspace, repoSlug, id, request) {
|
|
34437
|
+
return this.httpClient.put(this.buildPath(workspace, repoSlug, `/${id}`), request);
|
|
34438
|
+
}
|
|
34402
34439
|
async merge(workspace, repoSlug, id, request) {
|
|
34403
34440
|
return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/merge`), request);
|
|
34404
34441
|
}
|
|
@@ -34408,6 +34445,12 @@ class PullRequestRepository {
|
|
|
34408
34445
|
async decline(workspace, repoSlug, id) {
|
|
34409
34446
|
return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/decline`));
|
|
34410
34447
|
}
|
|
34448
|
+
async getDiff(workspace, repoSlug, id) {
|
|
34449
|
+
return this.httpClient.getText(this.buildPath(workspace, repoSlug, `/${id}/diff`));
|
|
34450
|
+
}
|
|
34451
|
+
async getDiffstat(workspace, repoSlug, id) {
|
|
34452
|
+
return this.httpClient.get(this.buildPath(workspace, repoSlug, `/${id}/diffstat`));
|
|
34453
|
+
}
|
|
34411
34454
|
}
|
|
34412
34455
|
// src/core/base-command.ts
|
|
34413
34456
|
class BaseCommand {
|
|
@@ -34446,7 +34489,7 @@ class LoginCommand extends BaseCommand {
|
|
|
34446
34489
|
configService;
|
|
34447
34490
|
userRepositoryFactory;
|
|
34448
34491
|
name = "login";
|
|
34449
|
-
description = "Authenticate with Bitbucket using an
|
|
34492
|
+
description = "Authenticate with Bitbucket using an API token";
|
|
34450
34493
|
constructor(configService, userRepositoryFactory, output) {
|
|
34451
34494
|
super(output);
|
|
34452
34495
|
this.configService = configService;
|
|
@@ -34454,18 +34497,18 @@ class LoginCommand extends BaseCommand {
|
|
|
34454
34497
|
}
|
|
34455
34498
|
async execute(options, context) {
|
|
34456
34499
|
const username = options.username || process.env.BB_USERNAME;
|
|
34457
|
-
const
|
|
34500
|
+
const apiToken = options.password || process.env.BB_API_TOKEN;
|
|
34458
34501
|
if (!username) {
|
|
34459
34502
|
const error = Result.err(new ValidationError("username", "Username is required. Use --username option or set BB_USERNAME environment variable."));
|
|
34460
34503
|
this.handleResult(error, context);
|
|
34461
34504
|
return error;
|
|
34462
34505
|
}
|
|
34463
|
-
if (!
|
|
34464
|
-
const error = Result.err(new ValidationError("password", "
|
|
34506
|
+
if (!apiToken) {
|
|
34507
|
+
const error = Result.err(new ValidationError("password", "API token is required. Use --password option or set BB_API_TOKEN environment variable."));
|
|
34465
34508
|
this.handleResult(error, context);
|
|
34466
34509
|
return error;
|
|
34467
34510
|
}
|
|
34468
|
-
const setResult = await this.configService.setCredentials({ username,
|
|
34511
|
+
const setResult = await this.configService.setCredentials({ username, apiToken });
|
|
34469
34512
|
if (!setResult.success) {
|
|
34470
34513
|
this.handleResult(setResult, context);
|
|
34471
34514
|
return setResult;
|
|
@@ -34520,7 +34563,7 @@ class StatusCommand extends BaseCommand {
|
|
|
34520
34563
|
return configResult;
|
|
34521
34564
|
}
|
|
34522
34565
|
const config = configResult.value;
|
|
34523
|
-
if (!config.username || !config.
|
|
34566
|
+
if (!config.username || !config.apiToken) {
|
|
34524
34567
|
const status2 = { authenticated: false };
|
|
34525
34568
|
this.handleResult(Result.ok(status2), context, () => {
|
|
34526
34569
|
this.output.info("Not logged in");
|
|
@@ -34574,8 +34617,8 @@ class TokenCommand extends BaseCommand {
|
|
|
34574
34617
|
}
|
|
34575
34618
|
return credentialsResult;
|
|
34576
34619
|
}
|
|
34577
|
-
const { username,
|
|
34578
|
-
const token = Buffer.from(`${username}:${
|
|
34620
|
+
const { username, apiToken } = credentialsResult.value;
|
|
34621
|
+
const token = Buffer.from(`${username}:${apiToken}`).toString("base64");
|
|
34579
34622
|
this.output.text(token);
|
|
34580
34623
|
return Result.ok(token);
|
|
34581
34624
|
}
|
|
@@ -35032,6 +35075,98 @@ class ViewPRCommand extends BaseCommand {
|
|
|
35032
35075
|
}
|
|
35033
35076
|
}
|
|
35034
35077
|
|
|
35078
|
+
// src/commands/pr/edit.command.ts
|
|
35079
|
+
import * as fs from "fs";
|
|
35080
|
+
class EditPRCommand extends BaseCommand {
|
|
35081
|
+
prRepository;
|
|
35082
|
+
contextService;
|
|
35083
|
+
gitService;
|
|
35084
|
+
name = "edit";
|
|
35085
|
+
description = "Edit a pull request";
|
|
35086
|
+
constructor(prRepository, contextService, gitService, output) {
|
|
35087
|
+
super(output);
|
|
35088
|
+
this.prRepository = prRepository;
|
|
35089
|
+
this.contextService = contextService;
|
|
35090
|
+
this.gitService = gitService;
|
|
35091
|
+
}
|
|
35092
|
+
async execute(options, context) {
|
|
35093
|
+
const repoContextResult = await this.contextService.requireRepoContext({
|
|
35094
|
+
...context.globalOptions,
|
|
35095
|
+
...options
|
|
35096
|
+
});
|
|
35097
|
+
if (!repoContextResult.success) {
|
|
35098
|
+
this.handleResult(repoContextResult, context);
|
|
35099
|
+
return repoContextResult;
|
|
35100
|
+
}
|
|
35101
|
+
const { workspace, repoSlug } = repoContextResult.value;
|
|
35102
|
+
let prId;
|
|
35103
|
+
if (options.id) {
|
|
35104
|
+
prId = parseInt(options.id, 10);
|
|
35105
|
+
} else {
|
|
35106
|
+
const branchResult = await this.gitService.getCurrentBranch();
|
|
35107
|
+
if (!branchResult.success) {
|
|
35108
|
+
this.handleResult(branchResult, context);
|
|
35109
|
+
return branchResult;
|
|
35110
|
+
}
|
|
35111
|
+
const currentBranch = branchResult.value;
|
|
35112
|
+
const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN");
|
|
35113
|
+
if (!prsResult.success) {
|
|
35114
|
+
this.handleResult(prsResult, context);
|
|
35115
|
+
return prsResult;
|
|
35116
|
+
}
|
|
35117
|
+
const matchingPR = prsResult.value.values.find((pr) => pr.source.branch.name === currentBranch);
|
|
35118
|
+
if (!matchingPR) {
|
|
35119
|
+
const error = new ValidationError("id", `No open pull request found for current branch '${currentBranch}'. Specify a PR ID explicitly.`);
|
|
35120
|
+
this.output.error(error.message);
|
|
35121
|
+
if (true) {
|
|
35122
|
+
process.exitCode = 1;
|
|
35123
|
+
}
|
|
35124
|
+
return Result.err(error);
|
|
35125
|
+
}
|
|
35126
|
+
prId = matchingPR.id;
|
|
35127
|
+
}
|
|
35128
|
+
let body = options.body;
|
|
35129
|
+
if (options.bodyFile) {
|
|
35130
|
+
try {
|
|
35131
|
+
body = fs.readFileSync(options.bodyFile, "utf-8");
|
|
35132
|
+
} catch (err) {
|
|
35133
|
+
const error = new ValidationError("bodyFile", `Failed to read file '${options.bodyFile}': ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
35134
|
+
this.output.error(error.message);
|
|
35135
|
+
if (true) {
|
|
35136
|
+
process.exitCode = 1;
|
|
35137
|
+
}
|
|
35138
|
+
return Result.err(error);
|
|
35139
|
+
}
|
|
35140
|
+
}
|
|
35141
|
+
if (!options.title && !body) {
|
|
35142
|
+
const error = new ValidationError("title", "At least one of --title or --body (or --body-file) is required.");
|
|
35143
|
+
this.output.error(error.message);
|
|
35144
|
+
if (true) {
|
|
35145
|
+
process.exitCode = 1;
|
|
35146
|
+
}
|
|
35147
|
+
return Result.err(error);
|
|
35148
|
+
}
|
|
35149
|
+
const request = {};
|
|
35150
|
+
if (options.title) {
|
|
35151
|
+
request.title = options.title;
|
|
35152
|
+
}
|
|
35153
|
+
if (body) {
|
|
35154
|
+
request.description = body;
|
|
35155
|
+
}
|
|
35156
|
+
const result = await this.prRepository.update(workspace, repoSlug, prId, request);
|
|
35157
|
+
this.handleResult(result, context, (pr) => {
|
|
35158
|
+
this.output.success(`Updated pull request #${pr.id}`);
|
|
35159
|
+
this.output.text(` ${source_default.dim("Title:")} ${pr.title}`);
|
|
35160
|
+
if (pr.description) {
|
|
35161
|
+
const truncatedDesc = pr.description.length > 100 ? pr.description.substring(0, 100) + "..." : pr.description;
|
|
35162
|
+
this.output.text(` ${source_default.dim("Description:")} ${truncatedDesc}`);
|
|
35163
|
+
}
|
|
35164
|
+
this.output.text(` ${source_default.dim("URL:")} ${pr.links.html.href}`);
|
|
35165
|
+
});
|
|
35166
|
+
return result;
|
|
35167
|
+
}
|
|
35168
|
+
}
|
|
35169
|
+
|
|
35035
35170
|
// src/commands/pr/merge.command.ts
|
|
35036
35171
|
class MergePRCommand extends BaseCommand {
|
|
35037
35172
|
prRepository;
|
|
@@ -35193,6 +35328,221 @@ class CheckoutPRCommand extends BaseCommand {
|
|
|
35193
35328
|
}
|
|
35194
35329
|
}
|
|
35195
35330
|
|
|
35331
|
+
// src/commands/pr/diff.command.ts
|
|
35332
|
+
import { exec } from "child_process";
|
|
35333
|
+
import { promisify } from "util";
|
|
35334
|
+
var execAsync = promisify(exec);
|
|
35335
|
+
|
|
35336
|
+
class DiffPRCommand extends BaseCommand {
|
|
35337
|
+
prRepository;
|
|
35338
|
+
contextService;
|
|
35339
|
+
gitService;
|
|
35340
|
+
name = "diff";
|
|
35341
|
+
description = "View pull request diff";
|
|
35342
|
+
constructor(prRepository, contextService, gitService, output) {
|
|
35343
|
+
super(output);
|
|
35344
|
+
this.prRepository = prRepository;
|
|
35345
|
+
this.contextService = contextService;
|
|
35346
|
+
this.gitService = gitService;
|
|
35347
|
+
}
|
|
35348
|
+
async execute(options, context) {
|
|
35349
|
+
const repoContextResult = await this.contextService.requireRepoContext({
|
|
35350
|
+
...context.globalOptions,
|
|
35351
|
+
...options
|
|
35352
|
+
});
|
|
35353
|
+
if (!repoContextResult.success) {
|
|
35354
|
+
this.handleResult(repoContextResult, context);
|
|
35355
|
+
return repoContextResult;
|
|
35356
|
+
}
|
|
35357
|
+
const { workspace, repoSlug } = repoContextResult.value;
|
|
35358
|
+
let prId;
|
|
35359
|
+
if (options.id) {
|
|
35360
|
+
prId = parseInt(options.id, 10);
|
|
35361
|
+
if (isNaN(prId)) {
|
|
35362
|
+
const error = new BBError({
|
|
35363
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
35364
|
+
message: "Invalid PR ID"
|
|
35365
|
+
});
|
|
35366
|
+
this.handleResult(Result.err(error), context);
|
|
35367
|
+
return Result.err(error);
|
|
35368
|
+
}
|
|
35369
|
+
} else {
|
|
35370
|
+
const currentBranchResult = await this.gitService.getCurrentBranch();
|
|
35371
|
+
if (!currentBranchResult.success) {
|
|
35372
|
+
const error = new BBError({
|
|
35373
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
35374
|
+
message: "No PR ID provided and could not determine current branch"
|
|
35375
|
+
});
|
|
35376
|
+
this.handleResult(Result.err(error), context);
|
|
35377
|
+
return Result.err(error);
|
|
35378
|
+
}
|
|
35379
|
+
const currentBranch = currentBranchResult.value;
|
|
35380
|
+
const prsResult = await this.prRepository.list(workspace, repoSlug, "OPEN", 100);
|
|
35381
|
+
if (!prsResult.success) {
|
|
35382
|
+
this.handleResult(prsResult, context);
|
|
35383
|
+
return prsResult;
|
|
35384
|
+
}
|
|
35385
|
+
const pr = prsResult.value.values.find((p) => p.source.branch.name === currentBranch);
|
|
35386
|
+
if (!pr) {
|
|
35387
|
+
const error = new BBError({
|
|
35388
|
+
code: 5002 /* VALIDATION_INVALID */,
|
|
35389
|
+
message: `No open pull request found for branch "${currentBranch}"`
|
|
35390
|
+
});
|
|
35391
|
+
this.handleResult(Result.err(error), context);
|
|
35392
|
+
return Result.err(error);
|
|
35393
|
+
}
|
|
35394
|
+
prId = pr.id;
|
|
35395
|
+
}
|
|
35396
|
+
if (options.web) {
|
|
35397
|
+
const prResult = await this.prRepository.get(workspace, repoSlug, prId);
|
|
35398
|
+
if (!prResult.success) {
|
|
35399
|
+
this.handleResult(prResult, context);
|
|
35400
|
+
return prResult;
|
|
35401
|
+
}
|
|
35402
|
+
const diffUrl = prResult.value.links.diff.href;
|
|
35403
|
+
const webUrl = diffUrl.replace(/api\.bitbucket\.org\/2\.0\/repositories\/(.*?)\/pullrequests\/(\d+)\/diff/, "bitbucket.org/$1/pull-requests/$2/diff");
|
|
35404
|
+
return this.openInBrowser(webUrl, context);
|
|
35405
|
+
}
|
|
35406
|
+
if (options.stat) {
|
|
35407
|
+
return this.showStat(workspace, repoSlug, prId, context);
|
|
35408
|
+
}
|
|
35409
|
+
if (options.nameOnly) {
|
|
35410
|
+
return this.showNameOnly(workspace, repoSlug, prId, context);
|
|
35411
|
+
}
|
|
35412
|
+
return this.showDiff(workspace, repoSlug, prId, options, context);
|
|
35413
|
+
}
|
|
35414
|
+
async openInBrowser(url, context) {
|
|
35415
|
+
if (context.globalOptions.json) {
|
|
35416
|
+
this.output.json({ url });
|
|
35417
|
+
return Result.ok({ diff: url });
|
|
35418
|
+
}
|
|
35419
|
+
this.output.info(`Opening ${url} in your browser...`);
|
|
35420
|
+
try {
|
|
35421
|
+
const platform = process.platform;
|
|
35422
|
+
let command;
|
|
35423
|
+
if (platform === "darwin") {
|
|
35424
|
+
command = `open "${url}"`;
|
|
35425
|
+
} else if (platform === "win32") {
|
|
35426
|
+
command = `start "" "${url}"`;
|
|
35427
|
+
} else {
|
|
35428
|
+
command = `xdg-open "${url}"`;
|
|
35429
|
+
}
|
|
35430
|
+
await execAsync(command);
|
|
35431
|
+
return Result.ok({ diff: url });
|
|
35432
|
+
} catch (error) {
|
|
35433
|
+
const bbError = new BBError({
|
|
35434
|
+
code: 3002 /* GIT_COMMAND_FAILED */,
|
|
35435
|
+
message: "Failed to open browser",
|
|
35436
|
+
cause: error instanceof Error ? error : undefined
|
|
35437
|
+
});
|
|
35438
|
+
this.handleResult(Result.err(bbError), context);
|
|
35439
|
+
return Result.err(bbError);
|
|
35440
|
+
}
|
|
35441
|
+
}
|
|
35442
|
+
async showStat(workspace, repoSlug, prId, context) {
|
|
35443
|
+
const diffstatResult = await this.prRepository.getDiffstat(workspace, repoSlug, prId);
|
|
35444
|
+
if (!diffstatResult.success) {
|
|
35445
|
+
this.handleResult(diffstatResult, context);
|
|
35446
|
+
return diffstatResult;
|
|
35447
|
+
}
|
|
35448
|
+
const diffstat = diffstatResult.value;
|
|
35449
|
+
const files = diffstat.values.map((file) => {
|
|
35450
|
+
const path = file.new?.path || file.old?.path || "unknown";
|
|
35451
|
+
return {
|
|
35452
|
+
path,
|
|
35453
|
+
additions: file.lines_added,
|
|
35454
|
+
deletions: file.lines_removed
|
|
35455
|
+
};
|
|
35456
|
+
});
|
|
35457
|
+
const totalAdditions = files.reduce((sum, f) => sum + f.additions, 0);
|
|
35458
|
+
const totalDeletions = files.reduce((sum, f) => sum + f.deletions, 0);
|
|
35459
|
+
const filesChanged = files.length;
|
|
35460
|
+
const result = {
|
|
35461
|
+
stat: {
|
|
35462
|
+
filesChanged,
|
|
35463
|
+
insertions: totalAdditions,
|
|
35464
|
+
deletions: totalDeletions,
|
|
35465
|
+
files
|
|
35466
|
+
}
|
|
35467
|
+
};
|
|
35468
|
+
this.handleResult(Result.ok(result), context, () => {
|
|
35469
|
+
for (const file of files) {
|
|
35470
|
+
const additions = file.additions > 0 ? source_default.green(`+${file.additions}`) : "";
|
|
35471
|
+
const deletions = file.deletions > 0 ? source_default.red(`-${file.deletions}`) : "";
|
|
35472
|
+
const stats = [additions, deletions].filter(Boolean).join(" ");
|
|
35473
|
+
this.output.text(`${file.path} ${stats ? `| ${stats}` : ""}`);
|
|
35474
|
+
}
|
|
35475
|
+
this.output.text("");
|
|
35476
|
+
const summary = [
|
|
35477
|
+
`${filesChanged} file${filesChanged !== 1 ? "s" : ""} changed`,
|
|
35478
|
+
totalAdditions > 0 ? source_default.green(`${totalAdditions} insertion${totalAdditions !== 1 ? "s" : ""}(+)`) : null,
|
|
35479
|
+
totalDeletions > 0 ? source_default.red(`${totalDeletions} deletion${totalDeletions !== 1 ? "s" : ""}(-)`) : null
|
|
35480
|
+
].filter(Boolean).join(", ");
|
|
35481
|
+
this.output.text(summary);
|
|
35482
|
+
});
|
|
35483
|
+
return Result.ok(result);
|
|
35484
|
+
}
|
|
35485
|
+
async showNameOnly(workspace, repoSlug, prId, context) {
|
|
35486
|
+
const diffstatResult = await this.prRepository.getDiffstat(workspace, repoSlug, prId);
|
|
35487
|
+
if (!diffstatResult.success) {
|
|
35488
|
+
this.handleResult(diffstatResult, context);
|
|
35489
|
+
return diffstatResult;
|
|
35490
|
+
}
|
|
35491
|
+
const diffstat = diffstatResult.value;
|
|
35492
|
+
const fileNames = diffstat.values.map((file) => file.new?.path || file.old?.path || "unknown");
|
|
35493
|
+
const result = {
|
|
35494
|
+
diff: fileNames.join(`
|
|
35495
|
+
`)
|
|
35496
|
+
};
|
|
35497
|
+
this.handleResult(Result.ok(result), context, () => {
|
|
35498
|
+
for (const fileName of fileNames) {
|
|
35499
|
+
this.output.text(fileName);
|
|
35500
|
+
}
|
|
35501
|
+
});
|
|
35502
|
+
return Result.ok(result);
|
|
35503
|
+
}
|
|
35504
|
+
async showDiff(workspace, repoSlug, prId, options, context) {
|
|
35505
|
+
const diffResult = await this.prRepository.getDiff(workspace, repoSlug, prId);
|
|
35506
|
+
if (!diffResult.success) {
|
|
35507
|
+
this.handleResult(diffResult, context);
|
|
35508
|
+
return diffResult;
|
|
35509
|
+
}
|
|
35510
|
+
const diff = diffResult.value;
|
|
35511
|
+
const result = { diff };
|
|
35512
|
+
this.handleResult(Result.ok(result), context, () => {
|
|
35513
|
+
const shouldColorize = this.shouldColorize(options.color);
|
|
35514
|
+
const colorizedDiff = shouldColorize ? this.colorizeDiff(diff) : diff;
|
|
35515
|
+
this.output.text(colorizedDiff);
|
|
35516
|
+
});
|
|
35517
|
+
return Result.ok(result);
|
|
35518
|
+
}
|
|
35519
|
+
shouldColorize(colorOption) {
|
|
35520
|
+
if (!colorOption || colorOption === "auto") {
|
|
35521
|
+
return process.stdout.isTTY ?? false;
|
|
35522
|
+
}
|
|
35523
|
+
return colorOption === "always";
|
|
35524
|
+
}
|
|
35525
|
+
colorizeDiff(diff) {
|
|
35526
|
+
const lines = diff.split(`
|
|
35527
|
+
`);
|
|
35528
|
+
return lines.map((line) => {
|
|
35529
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
35530
|
+
return source_default.green(line);
|
|
35531
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
35532
|
+
return source_default.red(line);
|
|
35533
|
+
} else if (line.startsWith("@@")) {
|
|
35534
|
+
return source_default.cyan(line);
|
|
35535
|
+
} else if (line.startsWith("diff --git")) {
|
|
35536
|
+
return source_default.bold(line);
|
|
35537
|
+
} else if (line.startsWith("index ") || line.startsWith("---") || line.startsWith("+++")) {
|
|
35538
|
+
return source_default.dim(line);
|
|
35539
|
+
}
|
|
35540
|
+
return line;
|
|
35541
|
+
}).join(`
|
|
35542
|
+
`);
|
|
35543
|
+
}
|
|
35544
|
+
}
|
|
35545
|
+
|
|
35196
35546
|
// src/types/config.ts
|
|
35197
35547
|
var SETTABLE_CONFIG_KEYS = ["defaultWorkspace"];
|
|
35198
35548
|
var READABLE_CONFIG_KEYS = ["username", "defaultWorkspace"];
|
|
@@ -35208,7 +35558,7 @@ class GetConfigCommand extends BaseCommand {
|
|
|
35208
35558
|
configService;
|
|
35209
35559
|
name = "get";
|
|
35210
35560
|
description = "Get a configuration value";
|
|
35211
|
-
static HIDDEN_KEYS = ["
|
|
35561
|
+
static HIDDEN_KEYS = ["apiToken"];
|
|
35212
35562
|
constructor(configService, output) {
|
|
35213
35563
|
super(output);
|
|
35214
35564
|
this.configService = configService;
|
|
@@ -35253,7 +35603,7 @@ class SetConfigCommand extends BaseCommand {
|
|
|
35253
35603
|
configService;
|
|
35254
35604
|
name = "set";
|
|
35255
35605
|
description = "Set a configuration value";
|
|
35256
|
-
static PROTECTED_KEYS = ["username", "
|
|
35606
|
+
static PROTECTED_KEYS = ["username", "apiToken"];
|
|
35257
35607
|
constructor(configService, output) {
|
|
35258
35608
|
super(output);
|
|
35259
35609
|
this.configService = configService;
|
|
@@ -35309,7 +35659,7 @@ class ListConfigCommand extends BaseCommand {
|
|
|
35309
35659
|
const displayConfig = {
|
|
35310
35660
|
username: config.username || "",
|
|
35311
35661
|
defaultWorkspace: config.defaultWorkspace || "",
|
|
35312
|
-
|
|
35662
|
+
apiToken: config.apiToken ? "********" : ""
|
|
35313
35663
|
};
|
|
35314
35664
|
this.handleResult(Result.ok(displayConfig), context, (data) => {
|
|
35315
35665
|
this.output.text(source_default.dim(`Config file: ${this.configService.getConfigPath()}`));
|
|
@@ -35470,6 +35820,13 @@ function bootstrap() {
|
|
|
35470
35820
|
const output = container.resolve(ServiceTokens.OutputService);
|
|
35471
35821
|
return new ViewPRCommand(prRepo, contextService, output);
|
|
35472
35822
|
});
|
|
35823
|
+
container.register(ServiceTokens.EditPRCommand, () => {
|
|
35824
|
+
const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
|
|
35825
|
+
const contextService = container.resolve(ServiceTokens.ContextService);
|
|
35826
|
+
const gitService = container.resolve(ServiceTokens.GitService);
|
|
35827
|
+
const output = container.resolve(ServiceTokens.OutputService);
|
|
35828
|
+
return new EditPRCommand(prRepo, contextService, gitService, output);
|
|
35829
|
+
});
|
|
35473
35830
|
container.register(ServiceTokens.MergePRCommand, () => {
|
|
35474
35831
|
const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
|
|
35475
35832
|
const contextService = container.resolve(ServiceTokens.ContextService);
|
|
@@ -35495,6 +35852,13 @@ function bootstrap() {
|
|
|
35495
35852
|
const output = container.resolve(ServiceTokens.OutputService);
|
|
35496
35853
|
return new CheckoutPRCommand(prRepo, contextService, gitService, output);
|
|
35497
35854
|
});
|
|
35855
|
+
container.register(ServiceTokens.DiffPRCommand, () => {
|
|
35856
|
+
const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
|
|
35857
|
+
const contextService = container.resolve(ServiceTokens.ContextService);
|
|
35858
|
+
const gitService = container.resolve(ServiceTokens.GitService);
|
|
35859
|
+
const output = container.resolve(ServiceTokens.OutputService);
|
|
35860
|
+
return new DiffPRCommand(prRepo, contextService, gitService, output);
|
|
35861
|
+
});
|
|
35498
35862
|
container.register(ServiceTokens.GetConfigCommand, () => {
|
|
35499
35863
|
const configService = container.resolve(ServiceTokens.ConfigService);
|
|
35500
35864
|
const output = container.resolve(ServiceTokens.OutputService);
|
|
@@ -35545,7 +35909,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
|
|
|
35545
35909
|
} else if (env2.prev === "repo") {
|
|
35546
35910
|
completions.push("clone", "create", "list", "view", "delete");
|
|
35547
35911
|
} else if (env2.prev === "pr") {
|
|
35548
|
-
completions.push("create", "list", "view", "merge", "approve", "decline", "checkout");
|
|
35912
|
+
completions.push("create", "list", "view", "edit", "merge", "approve", "decline", "checkout", "diff");
|
|
35549
35913
|
} else if (env2.prev === "config") {
|
|
35550
35914
|
completions.push("get", "set", "list");
|
|
35551
35915
|
} else if (env2.prev === "completion") {
|
|
@@ -35576,7 +35940,7 @@ function withGlobalOptions(options, context) {
|
|
|
35576
35940
|
var cli = new Command;
|
|
35577
35941
|
cli.name("bb").description("A command-line interface for Bitbucket Cloud").version(pkg.version).option("--json", "Output as JSON").option("-w, --workspace <workspace>", "Specify workspace").option("-r, --repo <repo>", "Specify repository");
|
|
35578
35942
|
var authCmd = new Command("auth").description("Authenticate with Bitbucket");
|
|
35579
|
-
authCmd.command("login").description("Authenticate with Bitbucket using an
|
|
35943
|
+
authCmd.command("login").description("Authenticate with Bitbucket using an API token").option("-u, --username <username>", "Bitbucket username").option("-p, --password <password>", "Bitbucket API token").action(async (options) => {
|
|
35580
35944
|
const cmd = container.resolve(ServiceTokens.LoginCommand);
|
|
35581
35945
|
const result = await cmd.execute(options, createContext(cli));
|
|
35582
35946
|
if (!result.success) {
|
|
@@ -35671,6 +36035,14 @@ prCmd.command("view <id>").description("View pull request details").action(async
|
|
|
35671
36035
|
process.exit(1);
|
|
35672
36036
|
}
|
|
35673
36037
|
});
|
|
36038
|
+
prCmd.command("edit [id]").description("Edit a pull request").option("-t, --title <title>", "New pull request title").option("-b, --body <body>", "New pull request description").option("-F, --body-file <file>", "Read description from file").action(async (id, options) => {
|
|
36039
|
+
const cmd = container.resolve(ServiceTokens.EditPRCommand);
|
|
36040
|
+
const context = createContext(cli);
|
|
36041
|
+
const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
|
|
36042
|
+
if (!result.success) {
|
|
36043
|
+
process.exit(1);
|
|
36044
|
+
}
|
|
36045
|
+
});
|
|
35674
36046
|
prCmd.command("merge <id>").description("Merge a pull request").option("-m, --message <message>", "Merge commit message").option("--close-source-branch", "Delete the source branch after merging").option("--strategy <strategy>", "Merge strategy (merge_commit, squash, fast_forward)").action(async (id, options) => {
|
|
35675
36047
|
const cmd = container.resolve(ServiceTokens.MergePRCommand);
|
|
35676
36048
|
const context = createContext(cli);
|
|
@@ -35703,6 +36075,14 @@ prCmd.command("checkout <id>").description("Checkout a pull request locally").ac
|
|
|
35703
36075
|
process.exit(1);
|
|
35704
36076
|
}
|
|
35705
36077
|
});
|
|
36078
|
+
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("-w, --web", "Open diff in web browser").action(async (id, options) => {
|
|
36079
|
+
const cmd = container.resolve(ServiceTokens.DiffPRCommand);
|
|
36080
|
+
const context = createContext(cli);
|
|
36081
|
+
const result = await cmd.execute(withGlobalOptions({ id, ...options }, context), context);
|
|
36082
|
+
if (!result.success) {
|
|
36083
|
+
process.exit(1);
|
|
36084
|
+
}
|
|
36085
|
+
});
|
|
35706
36086
|
cli.addCommand(prCmd);
|
|
35707
36087
|
var configCmd = new Command("config").description("Manage configuration");
|
|
35708
36088
|
configCmd.command("get <key>").description("Get a configuration value").action(async (key) => {
|