@pilatos/bitbucket-cli 1.0.0 → 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 +50 -230
- package/dist/index.js +156 -43
- 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,156 +77,55 @@ 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 API Token |
|
|
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 |
|
|
130
|
-
|
|
131
|
-
### Repositories
|
|
132
|
-
|
|
133
|
-
Clone, create, and manage your Bitbucket repositories.
|
|
134
|
-
|
|
135
|
-
| Command | Description |
|
|
136
|
-
|---------|-------------|
|
|
137
|
-
| `bb repo clone <repo>` | Clone a repository to your local machine |
|
|
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
|
-
```
|
|
80
|
+
## Features
|
|
187
81
|
|
|
188
|
-
|
|
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` |
|
|
189
89
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
110
|
+
## Authentication
|
|
213
111
|
|
|
214
|
-
The CLI uses **Bitbucket API Tokens** for secure authentication.
|
|
112
|
+
The CLI uses **Bitbucket API Tokens** for secure authentication.
|
|
215
113
|
|
|
216
|
-
> **Note**: As of September 9, 2025, Bitbucket app passwords are deprecated
|
|
114
|
+
> **Note**: As of September 9, 2025, Bitbucket app passwords are deprecated. Use API tokens instead.
|
|
217
115
|
|
|
218
|
-
###
|
|
116
|
+
### Create an API Token
|
|
219
117
|
|
|
220
118
|
1. Go to [Bitbucket API Tokens](https://bitbucket.org/account/settings/api-tokens/)
|
|
221
119
|
2. Click **"Create API token"**
|
|
222
|
-
3.
|
|
223
|
-
4.
|
|
224
|
-
- **Account:** Read
|
|
225
|
-
- **Repositories:** Read, Write, Admin (as needed)
|
|
226
|
-
- **Pull requests:** Read, Write
|
|
227
|
-
5. Click **"Create"**
|
|
228
|
-
6. **Copy the generated token** (you won't see it again!)
|
|
120
|
+
3. Select required scopes (Account, Repositories, Pull requests)
|
|
121
|
+
4. Copy the token
|
|
229
122
|
|
|
230
|
-
###
|
|
123
|
+
### Authenticate
|
|
231
124
|
|
|
232
125
|
```bash
|
|
233
126
|
bb auth login
|
|
234
127
|
```
|
|
235
128
|
|
|
236
|
-
Enter your Bitbucket username and the API Token when prompted.
|
|
237
|
-
|
|
238
|
-
### Step 3: Verify
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
bb auth status
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## Configuration
|
|
247
|
-
|
|
248
|
-
Configuration files are stored in platform-specific locations:
|
|
249
|
-
|
|
250
|
-
| Platform | Location |
|
|
251
|
-
|----------|----------|
|
|
252
|
-
| **macOS / Linux** | `~/.config/bb/config.json` |
|
|
253
|
-
| **Windows** | `%APPDATA%\bb\config.json` |
|
|
254
|
-
|
|
255
|
-
### Available Settings
|
|
256
|
-
|
|
257
|
-
```bash
|
|
258
|
-
# Set your default workspace
|
|
259
|
-
bb config set workspace myworkspace
|
|
260
|
-
|
|
261
|
-
# Set your default repository
|
|
262
|
-
bb config set repo myrepo
|
|
263
|
-
|
|
264
|
-
# View all settings
|
|
265
|
-
bb config list
|
|
266
|
-
```
|
|
267
|
-
|
|
268
129
|
---
|
|
269
130
|
|
|
270
131
|
## Examples
|
|
@@ -272,53 +133,38 @@ bb config list
|
|
|
272
133
|
### Daily Workflow
|
|
273
134
|
|
|
274
135
|
```bash
|
|
275
|
-
#
|
|
276
|
-
bb pr list
|
|
136
|
+
# Check open PRs
|
|
137
|
+
bb pr list
|
|
277
138
|
|
|
278
|
-
# Review
|
|
139
|
+
# Review and merge
|
|
279
140
|
bb pr view 42
|
|
280
|
-
|
|
281
|
-
# Approve and merge
|
|
282
141
|
bb pr approve 42
|
|
283
142
|
bb pr merge 42
|
|
284
143
|
```
|
|
285
144
|
|
|
286
|
-
###
|
|
145
|
+
### Scripting with JSON
|
|
287
146
|
|
|
288
147
|
```bash
|
|
289
|
-
#
|
|
290
|
-
bb pr
|
|
291
|
-
--title "feat: Add user notifications" \
|
|
292
|
-
--description "Implements real-time notifications using WebSockets" \
|
|
293
|
-
--source feature/notifications \
|
|
294
|
-
--destination main
|
|
295
|
-
```
|
|
148
|
+
# Get all open PR titles
|
|
149
|
+
bb pr list --json | jq '.[].title'
|
|
296
150
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
```bash
|
|
300
|
-
# Get all open PRs as JSON for processing
|
|
301
|
-
bb pr list --state OPEN --json | jq '.[] | .title'
|
|
302
|
-
|
|
303
|
-
# List repos and filter by name
|
|
151
|
+
# Filter repos by name
|
|
304
152
|
bb repo list --json | jq '.[] | select(.name | contains("api"))'
|
|
305
153
|
```
|
|
306
154
|
|
|
307
|
-
###
|
|
155
|
+
### CI/CD Usage
|
|
308
156
|
|
|
309
157
|
```bash
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
bb
|
|
313
|
-
|
|
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
|
|
314
162
|
```
|
|
315
163
|
|
|
316
164
|
---
|
|
317
165
|
|
|
318
166
|
## Development
|
|
319
167
|
|
|
320
|
-
Want to contribute or run locally? Here's how:
|
|
321
|
-
|
|
322
168
|
```bash
|
|
323
169
|
# Clone the repository
|
|
324
170
|
git clone https://github.com/0pilatos0/bitbucket-cli.git
|
|
@@ -335,59 +181,33 @@ bun test
|
|
|
335
181
|
|
|
336
182
|
# Build for production
|
|
337
183
|
bun run build
|
|
338
|
-
|
|
339
|
-
# Generate API client from OpenAPI spec
|
|
340
|
-
bun run generate:api
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### Project Structure
|
|
344
|
-
|
|
345
|
-
```
|
|
346
|
-
bitbucket-cli/
|
|
347
|
-
├── src/
|
|
348
|
-
│ ├── commands/ # Command implementations
|
|
349
|
-
│ ├── core/ # Core utilities and base classes
|
|
350
|
-
│ └── index.ts # Entry point
|
|
351
|
-
├── tests/ # Test files
|
|
352
|
-
├── docs/ # Documentation site (Astro)
|
|
353
|
-
└── specs/ # OpenAPI specifications
|
|
354
184
|
```
|
|
355
185
|
|
|
356
186
|
---
|
|
357
187
|
|
|
358
188
|
## Contributing
|
|
359
189
|
|
|
360
|
-
We welcome contributions
|
|
361
|
-
|
|
362
|
-
- Reporting bugs
|
|
363
|
-
- Suggesting new features
|
|
364
|
-
- Improving documentation
|
|
365
|
-
- Submitting pull requests
|
|
366
|
-
|
|
367
|
-
Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
368
|
-
|
|
369
|
-
### Quick Contribution Steps
|
|
190
|
+
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
370
191
|
|
|
371
192
|
1. Fork the repository
|
|
372
|
-
2. Create a feature branch
|
|
193
|
+
2. Create a feature branch
|
|
373
194
|
3. Make your changes
|
|
374
195
|
4. Run tests (`bun test`)
|
|
375
|
-
5.
|
|
376
|
-
6. Push and open a Pull Request
|
|
196
|
+
5. Submit a Pull Request
|
|
377
197
|
|
|
378
198
|
---
|
|
379
199
|
|
|
380
200
|
## Acknowledgments
|
|
381
201
|
|
|
382
|
-
- Inspired by [GitHub CLI (`gh`)](https://cli.github.com/)
|
|
383
|
-
- 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)
|
|
384
204
|
- Uses the [Bitbucket Cloud REST API](https://developer.atlassian.com/cloud/bitbucket/rest/)
|
|
385
205
|
|
|
386
206
|
---
|
|
387
207
|
|
|
388
208
|
## License
|
|
389
209
|
|
|
390
|
-
|
|
210
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
391
211
|
|
|
392
212
|
---
|
|
393
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) => {
|
|
@@ -32700,7 +32700,7 @@ var require_promisify = __commonJS((exports) => {
|
|
|
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
32796
|
var { promisify: promisify2 } = require_promisify();
|
|
32797
|
-
var readFile = promisify2(
|
|
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
32844
|
var { promisify: promisify2 } = require_promisify();
|
|
32845
32845
|
var mkdirp = promisify2(require_mkdirp());
|
|
32846
32846
|
var { tabtabDebug, systemShell, exists } = require_utils2();
|
|
32847
32847
|
var debug = tabtabDebug("tabtab:installer");
|
|
32848
|
-
var readFile = promisify2(
|
|
32849
|
-
var writeFile = promisify2(
|
|
32850
|
-
var unlink = promisify2(
|
|
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,6 +33258,7 @@ 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",
|
|
@@ -34298,11 +34299,12 @@ class HttpClient {
|
|
|
34298
34299
|
}
|
|
34299
34300
|
async handleResponse(response) {
|
|
34300
34301
|
if (!response.ok) {
|
|
34302
|
+
const textBody = await response.text();
|
|
34301
34303
|
let errorBody;
|
|
34302
34304
|
try {
|
|
34303
|
-
errorBody =
|
|
34305
|
+
errorBody = JSON.parse(textBody);
|
|
34304
34306
|
} catch {
|
|
34305
|
-
errorBody =
|
|
34307
|
+
errorBody = textBody;
|
|
34306
34308
|
}
|
|
34307
34309
|
const message = this.extractErrorMessage(errorBody, response.statusText);
|
|
34308
34310
|
return Result.err(new APIError(message, response.status, errorBody, {
|
|
@@ -34325,11 +34327,12 @@ class HttpClient {
|
|
|
34325
34327
|
}
|
|
34326
34328
|
async handleTextResponse(response) {
|
|
34327
34329
|
if (!response.ok) {
|
|
34330
|
+
const textBody = await response.text();
|
|
34328
34331
|
let errorBody;
|
|
34329
34332
|
try {
|
|
34330
|
-
errorBody =
|
|
34333
|
+
errorBody = JSON.parse(textBody);
|
|
34331
34334
|
} catch {
|
|
34332
|
-
errorBody =
|
|
34335
|
+
errorBody = textBody;
|
|
34333
34336
|
}
|
|
34334
34337
|
const message = this.extractErrorMessage(errorBody, response.statusText);
|
|
34335
34338
|
return Result.err(new APIError(message, response.status, errorBody, {
|
|
@@ -34430,6 +34433,9 @@ class PullRequestRepository {
|
|
|
34430
34433
|
async create(workspace, repoSlug, request) {
|
|
34431
34434
|
return this.httpClient.post(this.buildPath(workspace, repoSlug), request);
|
|
34432
34435
|
}
|
|
34436
|
+
async update(workspace, repoSlug, id, request) {
|
|
34437
|
+
return this.httpClient.put(this.buildPath(workspace, repoSlug, `/${id}`), request);
|
|
34438
|
+
}
|
|
34433
34439
|
async merge(workspace, repoSlug, id, request) {
|
|
34434
34440
|
return this.httpClient.post(this.buildPath(workspace, repoSlug, `/${id}/merge`), request);
|
|
34435
34441
|
}
|
|
@@ -35069,6 +35075,98 @@ class ViewPRCommand extends BaseCommand {
|
|
|
35069
35075
|
}
|
|
35070
35076
|
}
|
|
35071
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
|
+
|
|
35072
35170
|
// src/commands/pr/merge.command.ts
|
|
35073
35171
|
class MergePRCommand extends BaseCommand {
|
|
35074
35172
|
prRepository;
|
|
@@ -35722,6 +35820,13 @@ function bootstrap() {
|
|
|
35722
35820
|
const output = container.resolve(ServiceTokens.OutputService);
|
|
35723
35821
|
return new ViewPRCommand(prRepo, contextService, output);
|
|
35724
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
|
+
});
|
|
35725
35830
|
container.register(ServiceTokens.MergePRCommand, () => {
|
|
35726
35831
|
const prRepo = container.resolve(ServiceTokens.PullRequestRepository);
|
|
35727
35832
|
const contextService = container.resolve(ServiceTokens.ContextService);
|
|
@@ -35804,7 +35909,7 @@ if (process.argv.includes("--get-yargs-completions") || process.env.COMP_LINE) {
|
|
|
35804
35909
|
} else if (env2.prev === "repo") {
|
|
35805
35910
|
completions.push("clone", "create", "list", "view", "delete");
|
|
35806
35911
|
} else if (env2.prev === "pr") {
|
|
35807
|
-
completions.push("create", "list", "view", "merge", "approve", "decline", "checkout", "diff");
|
|
35912
|
+
completions.push("create", "list", "view", "edit", "merge", "approve", "decline", "checkout", "diff");
|
|
35808
35913
|
} else if (env2.prev === "config") {
|
|
35809
35914
|
completions.push("get", "set", "list");
|
|
35810
35915
|
} else if (env2.prev === "completion") {
|
|
@@ -35930,6 +36035,14 @@ prCmd.command("view <id>").description("View pull request details").action(async
|
|
|
35930
36035
|
process.exit(1);
|
|
35931
36036
|
}
|
|
35932
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
|
+
});
|
|
35933
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) => {
|
|
35934
36047
|
const cmd = container.resolve(ServiceTokens.MergePRCommand);
|
|
35935
36048
|
const context = createContext(cli);
|