vnxt 1.4.5
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/.vnxtrc.json +8 -0
- package/LICENSE +21 -0
- package/README.md +379 -0
- package/package.json +46 -0
- package/vnxt.js +443 -0
package/.vnxtrc.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nate Orrow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
<p>
|
|
2
|
+
<img src="./docs/logos/vnxt_light_logo.png" alt="vnxt logo" width="200">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# vnxt (vx)
|
|
6
|
+
|
|
7
|
+
A lightweight CLI tool for automated version bumping with changelog generation and git integration.
|
|
8
|
+
|
|
9
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Features
|
|
10
|
+
|
|
11
|
+
- š Automatic semantic version detection from commit messages
|
|
12
|
+
- š Automatic CHANGELOG.md generation
|
|
13
|
+
- š·ļø Git tag annotation
|
|
14
|
+
- š Pre-flight checks for clean working directory
|
|
15
|
+
- š¬ Dry-run mode to preview changes
|
|
16
|
+
- š Release notes generation
|
|
17
|
+
- āļø Project-level configuration support
|
|
18
|
+
- š¬ Interactive mode when no arguments provided
|
|
19
|
+
|
|
20
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Installation
|
|
21
|
+
|
|
22
|
+
### Global Installation
|
|
23
|
+
|
|
24
|
+
**Bash/PowerShell:**
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g vnxt
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
After installation, you can use either `vnxt` or the shorter alias `vx`:
|
|
30
|
+
```bash
|
|
31
|
+
vnxt --help
|
|
32
|
+
vx --help # Same thing, shorter!
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Local Installation (from source)
|
|
36
|
+
|
|
37
|
+
**Bash/macOS/Linux:**
|
|
38
|
+
```bash
|
|
39
|
+
# Clone the repository
|
|
40
|
+
git clone https://github.com/n-orrow/vnxt.git
|
|
41
|
+
cd vnxt
|
|
42
|
+
|
|
43
|
+
# Install globally via npm link
|
|
44
|
+
chmod +x vnxt.js
|
|
45
|
+
npm link
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**PowerShell/Windows:**
|
|
49
|
+
```powershell
|
|
50
|
+
# Clone the repository
|
|
51
|
+
git clone https://github.com/n-orrow/vnxt.git
|
|
52
|
+
cd vnxt
|
|
53
|
+
|
|
54
|
+
# Install globally via npm link
|
|
55
|
+
npm link
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Usage
|
|
59
|
+
|
|
60
|
+
### Basic Examples
|
|
61
|
+
|
|
62
|
+
**Bash/PowerShell:**
|
|
63
|
+
```bash
|
|
64
|
+
# Simple version bump (auto-detects patch from "fix:")
|
|
65
|
+
vnxt -m "fix: resolve RFID reader bug"
|
|
66
|
+
# or use the shorter alias:
|
|
67
|
+
vx -m "fix: resolve RFID reader bug"
|
|
68
|
+
|
|
69
|
+
# Feature addition (auto-detects minor from "feat:")
|
|
70
|
+
vx -m "feat: add heatmap visualization"
|
|
71
|
+
|
|
72
|
+
# Breaking change (auto-detects major from "BREAKING")
|
|
73
|
+
vx -m "BREAKING: redesign API structure"
|
|
74
|
+
|
|
75
|
+
# With changelog and push to remote
|
|
76
|
+
vx -m "feat: add new dashboard" -c -p
|
|
77
|
+
|
|
78
|
+
# Interactive mode (prompts for input)
|
|
79
|
+
vx
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Command Line Options
|
|
83
|
+
|
|
84
|
+
All options work with both `vnxt` and `vx`:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
-m, --message <msg> Commit message (required unless using interactive mode)
|
|
88
|
+
-t, --type <type> Version type: patch, minor, major (auto-detected from message)
|
|
89
|
+
-v, --version <ver> Set specific version (e.g., 2.0.0-beta.1)
|
|
90
|
+
-p, --push Push to remote with tags
|
|
91
|
+
-c, --changelog Update CHANGELOG.md
|
|
92
|
+
-d, --dry-run Show what would happen without making changes
|
|
93
|
+
-a, --all [mode] Stage files before versioning (prompts if no mode)
|
|
94
|
+
Modes: tracked, all, interactive (i), patch (p)
|
|
95
|
+
-r, --release Generate release notes file
|
|
96
|
+
-h, --help Show help message
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Automatic Version Detection
|
|
100
|
+
|
|
101
|
+
vnxt automatically detects the version bump type from your commit message:
|
|
102
|
+
|
|
103
|
+
- `major:` ā **major** version bump
|
|
104
|
+
- `minor:` ā **minor** version bump
|
|
105
|
+
- `patch:` ā **patch** version bump
|
|
106
|
+
- `feat:` or `feature:` ā **minor** version bump
|
|
107
|
+
- `fix:` ā **patch** version bump
|
|
108
|
+
- `BREAKING:` or contains `BREAKING` ā **major** version bump
|
|
109
|
+
|
|
110
|
+
You can override this with the `-t` flag.
|
|
111
|
+
|
|
112
|
+
### Dry Run
|
|
113
|
+
|
|
114
|
+
Preview what will happen without making changes:
|
|
115
|
+
|
|
116
|
+
**Bash/PowerShell:**
|
|
117
|
+
```bash
|
|
118
|
+
vx -m "feat: new feature" -d
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Output:
|
|
122
|
+
```
|
|
123
|
+
š¬ DRY RUN MODE - No changes will be made
|
|
124
|
+
|
|
125
|
+
Would perform the following actions:
|
|
126
|
+
1. Bump minor version
|
|
127
|
+
2. Commit with message: "feat: new feature"
|
|
128
|
+
3. Create git tag with annotation
|
|
129
|
+
4. (Skipping push - use --push to enable)
|
|
130
|
+
|
|
131
|
+
ā Dry run complete. Use without -d to apply changes.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Custom Versions
|
|
135
|
+
|
|
136
|
+
Set a specific version number (useful for pre-releases):
|
|
137
|
+
|
|
138
|
+
**Bash/PowerShell:**
|
|
139
|
+
```bash
|
|
140
|
+
vx -v 2.0.0-beta.1 -m "beta: initial release candidate"
|
|
141
|
+
vx -v 1.5.0-rc.2 -m "release candidate 2"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Changelog Generation
|
|
145
|
+
|
|
146
|
+
Automatically update CHANGELOG.md with version history:
|
|
147
|
+
|
|
148
|
+
**Bash/PowerShell:**
|
|
149
|
+
```bash
|
|
150
|
+
vx -m "feat: add user authentication" -c
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Creates/updates CHANGELOG.md:
|
|
154
|
+
```markdown
|
|
155
|
+
# Changelog
|
|
156
|
+
|
|
157
|
+
## [1.2.0] - 2024-02-10
|
|
158
|
+
- feat: add user authentication
|
|
159
|
+
|
|
160
|
+
## [1.1.0] - 2024-02-09
|
|
161
|
+
- feat: add dashboard
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Release Notes
|
|
165
|
+
|
|
166
|
+
Generate a formatted release notes file:
|
|
167
|
+
|
|
168
|
+
**Bash/PowerShell:**
|
|
169
|
+
```bash
|
|
170
|
+
vx -m "feat: major feature release" -r
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Creates `release-notes-v1.2.0.md`:
|
|
174
|
+
```markdown
|
|
175
|
+
# Release v1.2.0
|
|
176
|
+
|
|
177
|
+
Released: 2024-02-10
|
|
178
|
+
|
|
179
|
+
## Changes
|
|
180
|
+
feat: major feature release
|
|
181
|
+
|
|
182
|
+
## Installation
|
|
183
|
+
npm install your-package@1.2.0
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### File Staging Options
|
|
187
|
+
|
|
188
|
+
vnxt offers flexible file staging with the `-a` flag:
|
|
189
|
+
|
|
190
|
+
**Bash/PowerShell:**
|
|
191
|
+
```bash
|
|
192
|
+
# Interactive prompt (asks which mode to use)
|
|
193
|
+
vx -m "chore: update" -a
|
|
194
|
+
|
|
195
|
+
# Specific modes
|
|
196
|
+
vx -m "fix: bug" -a tracked # Stage tracked files only (git add -u)
|
|
197
|
+
vx -m "feat: new" -a all # Stage all changes (git add -A)
|
|
198
|
+
vx -m "refactor: code" -a i # Interactive selection (git add -i)
|
|
199
|
+
vx -m "fix: typo" -a p # Patch mode (git add -p)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Staging Modes:**
|
|
203
|
+
- `tracked` - Only staged tracked files that have been modified/deleted (default)
|
|
204
|
+
- `all` - Stages all changes, respects `.gitignore` for new files
|
|
205
|
+
- `interactive` or `i` - Opens git's interactive staging mode
|
|
206
|
+
- `patch` or `p` - Opens patch mode for selective staging
|
|
207
|
+
|
|
208
|
+
**Note:** If you run `vx` without any files staged and without the `-a` flag, vnxt will prompt you interactively to choose a staging mode.
|
|
209
|
+
|
|
210
|
+
### Complete Workflow Example
|
|
211
|
+
|
|
212
|
+
**Bash/PowerShell:**
|
|
213
|
+
```bash
|
|
214
|
+
# Make changes to your code
|
|
215
|
+
# ...
|
|
216
|
+
|
|
217
|
+
# Dry run to preview
|
|
218
|
+
vx -m "feat: add new API endpoint" -d
|
|
219
|
+
|
|
220
|
+
# Execute with changelog, release notes, and push
|
|
221
|
+
vx -m "feat: add new API endpoint" -c -r -p
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Configuration
|
|
225
|
+
|
|
226
|
+
Create a `.vnxtrc.json` file in your project root to set defaults:
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"autoChangelog": true,
|
|
230
|
+
"defaultType": "patch",
|
|
231
|
+
"requireCleanWorkingDir": true,
|
|
232
|
+
"autoPush": false
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Configuration Options
|
|
237
|
+
|
|
238
|
+
| Option | Type | Default | Description |
|
|
239
|
+
|--------|------|---------|-------------|
|
|
240
|
+
| `autoChangelog` | boolean | `false` | Automatically update CHANGELOG.md on every bump |
|
|
241
|
+
| `defaultType` | string | `"patch"` | Default version bump type if not auto-detected |
|
|
242
|
+
| `requireCleanWorkingDir` | boolean | `true` | Require clean git working directory before bumping |
|
|
243
|
+
| `autoPush` | boolean | `false` | Automatically push to remote after bumping |
|
|
244
|
+
|
|
245
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Pre-flight Checks
|
|
246
|
+
|
|
247
|
+
vnxt performs several checks before making changes:
|
|
248
|
+
|
|
249
|
+
- ā
Verifies no uncommitted changes (unless using `-a`)
|
|
250
|
+
- ā
Warns if not on main/master branch
|
|
251
|
+
- ā
Checks for remote repository (if pushing)
|
|
252
|
+
|
|
253
|
+
Example output:
|
|
254
|
+
```
|
|
255
|
+
š Running pre-flight checks...
|
|
256
|
+
|
|
257
|
+
ā ļø Warning: You're on branch 'feature/new-dashboard', not main/master
|
|
258
|
+
ā
Pre-flight checks passed
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Interactive Mode
|
|
262
|
+
|
|
263
|
+
Run `vnxt` (or `vx`) without arguments for guided prompts:
|
|
264
|
+
|
|
265
|
+
**Bash/PowerShell:**
|
|
266
|
+
```bash
|
|
267
|
+
vx
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Output:
|
|
271
|
+
```
|
|
272
|
+
š¤ Interactive mode
|
|
273
|
+
|
|
274
|
+
Commit message: feat: add new feature
|
|
275
|
+
Version type (patch/minor/major) [patch]: minor
|
|
276
|
+
|
|
277
|
+
š Auto-detected: minor version bump (feature)
|
|
278
|
+
|
|
279
|
+
š Running pre-flight checks...
|
|
280
|
+
...
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Workflow Examples
|
|
284
|
+
|
|
285
|
+
### Quick Fix
|
|
286
|
+
|
|
287
|
+
**Bash/PowerShell:**
|
|
288
|
+
```bash
|
|
289
|
+
vx -m "fix: resolve login bug"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Feature Release
|
|
293
|
+
|
|
294
|
+
**Bash/PowerShell:**
|
|
295
|
+
```bash
|
|
296
|
+
vx -m "feat: add dashboard analytics" -c -p
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Major Release with Full Documentation
|
|
300
|
+
|
|
301
|
+
**Bash/PowerShell:**
|
|
302
|
+
```bash
|
|
303
|
+
vx -m "BREAKING: new API structure" -c -r -p
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Local Development (No Push)
|
|
307
|
+
|
|
308
|
+
**Bash/PowerShell:**
|
|
309
|
+
```bash
|
|
310
|
+
vx -m "chore: refactor code" -a
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Troubleshooting
|
|
314
|
+
|
|
315
|
+
### Permission Denied (Windows PowerShell)
|
|
316
|
+
|
|
317
|
+
If you get execution policy errors:
|
|
318
|
+
```powershell
|
|
319
|
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Uncommitted Changes Error
|
|
323
|
+
|
|
324
|
+
Either commit your changes first, or use the `-a` flag to stage all changes:
|
|
325
|
+
|
|
326
|
+
**Bash/PowerShell:**
|
|
327
|
+
```bash
|
|
328
|
+
vx -m "your message" -a
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Command Not Found After Installation
|
|
332
|
+
|
|
333
|
+
Make sure npm's global bin directory is in your PATH:
|
|
334
|
+
|
|
335
|
+
**Bash:**
|
|
336
|
+
```bash
|
|
337
|
+
npm config get prefix
|
|
338
|
+
# Add the bin subdirectory to your PATH
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**PowerShell:**
|
|
342
|
+
```powershell
|
|
343
|
+
npm config get prefix
|
|
344
|
+
# Add the bin subdirectory to your PATH in System Environment Variables
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Requirements
|
|
348
|
+
|
|
349
|
+
- Node.js 12.x or higher
|
|
350
|
+
- npm 6.x or higher
|
|
351
|
+
- Git installed and configured
|
|
352
|
+
|
|
353
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Version Management
|
|
354
|
+
|
|
355
|
+
This project uses [vnxt](https://vnxt.dev) for version bumping with the following configuration:
|
|
356
|
+
|
|
357
|
+
- Auto-push enabled (`autoPush: true`)
|
|
358
|
+
- Auto-changelog enabled (`autoChangelog: true`)
|
|
359
|
+
- Clean working directory not required (`requireCleanWorkingDir: false`)
|
|
360
|
+
|
|
361
|
+
See `.vnxtrc.json` for full configuration.
|
|
362
|
+
|
|
363
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Author
|
|
364
|
+
|
|
365
|
+
Nate Orrow - Software Developer
|
|
366
|
+
|
|
367
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> License
|
|
368
|
+
|
|
369
|
+
MIT License - see [LICENSE](LICENSE) file for details
|
|
370
|
+
|
|
371
|
+
## <img src="./docs/logos/caret-38x38.png" width="24" align="center"> Contributing
|
|
372
|
+
|
|
373
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
374
|
+
|
|
375
|
+
1. Fork the repository
|
|
376
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
377
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
378
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
379
|
+
5. Open a Pull Request
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vnxt",
|
|
3
|
+
"version": "1.4.5",
|
|
4
|
+
"description": "Version incrementation CLI tool with built in git commit, push and changelog generation",
|
|
5
|
+
"main": "vnxt.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vnxt": "./vnxt.js",
|
|
8
|
+
"vx": "./vnxt.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"release": "vnxt -m 'release' -c -p"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"version",
|
|
16
|
+
"bump",
|
|
17
|
+
"semver",
|
|
18
|
+
"git",
|
|
19
|
+
"changelog",
|
|
20
|
+
"cli",
|
|
21
|
+
"release",
|
|
22
|
+
"npm"
|
|
23
|
+
],
|
|
24
|
+
"author": "Nate Orrow | Nate@Orrow.uk",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/n-orrow/vnxt.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/n-orrow/vnxt/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://vnxt.dev",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=12.0.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"vnxt.js",
|
|
39
|
+
".vnxtrc.json",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"jest": "^29.7.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/vnxt.js
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const {execSync} = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
|
|
7
|
+
// Parse command line arguments
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
|
|
10
|
+
// Helper to parse flags
|
|
11
|
+
function getFlag(flag, short) {
|
|
12
|
+
const index = args.indexOf(flag) !== -1 ? args.indexOf(flag) : args.indexOf(short);
|
|
13
|
+
if (index === -1) return null;
|
|
14
|
+
return args[index + 1] || true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function hasFlag(flag, short) {
|
|
18
|
+
return args.includes(flag) || args.includes(short);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Load config file if exists
|
|
22
|
+
let config = {
|
|
23
|
+
autoChangelog: true,
|
|
24
|
+
defaultType: 'patch',
|
|
25
|
+
requireCleanWorkingDir: false,
|
|
26
|
+
autoPush: true,
|
|
27
|
+
defaultStageMode: 'tracked',
|
|
28
|
+
tagPrefix: 'v'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (fs.existsSync('.vnxtrc.json')) {
|
|
32
|
+
const userConfig = JSON.parse(fs.readFileSync('.vnxtrc.json', 'utf8'));
|
|
33
|
+
config = {...config, ...userConfig};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Parse arguments
|
|
37
|
+
let message = getFlag('--message', '-m');
|
|
38
|
+
let type = getFlag('--type', '-t') || config.defaultType;
|
|
39
|
+
let customVersion = getFlag('--version', '-v');
|
|
40
|
+
let dryRun = hasFlag('--dry-run', '-d');
|
|
41
|
+
let noPush = hasFlag('--no-push', '-dnp');
|
|
42
|
+
let push = noPush ? false : (hasFlag('--push', '-p') || config.autoPush);
|
|
43
|
+
let generateChangelog = hasFlag('--changelog', '-c') || config.autoChangelog;
|
|
44
|
+
const addAllFlag = getFlag('--all', '-a');
|
|
45
|
+
let addMode = null; // Will be set to: 'tracked', 'all', 'interactive', 'patch', or null
|
|
46
|
+
let promptForStaging = false; // If true, prompt user for staging mode
|
|
47
|
+
if (addAllFlag) {
|
|
48
|
+
// If -a has a value, use it as the mode
|
|
49
|
+
if (typeof addAllFlag === 'string') {
|
|
50
|
+
const mode = addAllFlag.toLowerCase();
|
|
51
|
+
if (['tracked', 'all', 'a', 'interactive', 'i', 'patch', 'p'].includes(mode)) {
|
|
52
|
+
if (mode === 'a') addMode = 'all';
|
|
53
|
+
else if (mode === 'i') addMode = 'interactive';
|
|
54
|
+
else if (mode === 'p') addMode = 'patch';
|
|
55
|
+
else addMode = mode;
|
|
56
|
+
} else {
|
|
57
|
+
console.error(`Error: Invalid add mode '${addAllFlag}'. Use: tracked, all, interactive (i), or patch (p)`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// If -a has no value, we'll prompt the user later
|
|
62
|
+
promptForStaging = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let generateReleaseNotes = hasFlag('--release', '-r');
|
|
66
|
+
|
|
67
|
+
// Interactive mode helper
|
|
68
|
+
async function prompt(question) {
|
|
69
|
+
const rl = readline.createInterface({
|
|
70
|
+
input: process.stdin, output: process.stdout
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return new Promise(resolve => {
|
|
74
|
+
rl.question(question, answer => {
|
|
75
|
+
rl.close();
|
|
76
|
+
resolve(answer);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Main function
|
|
82
|
+
async function main() {
|
|
83
|
+
try {
|
|
84
|
+
// Interactive mode if no message provided
|
|
85
|
+
if (!message) {
|
|
86
|
+
console.log('š¤ Interactive mode\n');
|
|
87
|
+
|
|
88
|
+
message = await prompt('Commit message: ');
|
|
89
|
+
if (!message) {
|
|
90
|
+
console.error('Error: Commit message is required');
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const typeInput = await prompt('Version type (patch/minor/major) [auto-detect]: ');
|
|
95
|
+
if (typeInput && ['patch', 'minor', 'major'].includes(typeInput)) {
|
|
96
|
+
type = typeInput;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const changelogInput = await prompt('Update CHANGELOG.md? (y/n) [n]: ');
|
|
100
|
+
generateChangelog = changelogInput.toLowerCase() === 'y' || changelogInput.toLowerCase() === 'yes' || generateChangelog;
|
|
101
|
+
|
|
102
|
+
const releaseNotesInput = await prompt('Generate release notes? (y/n) [n]: ');
|
|
103
|
+
generateReleaseNotes = releaseNotesInput.toLowerCase() === 'y' || releaseNotesInput.toLowerCase() === 'yes';
|
|
104
|
+
|
|
105
|
+
const pushInput = await prompt('Push to remote? (y/n) [n]: ');
|
|
106
|
+
push = pushInput.toLowerCase() === 'y' || pushInput.toLowerCase() === 'yes' || push;
|
|
107
|
+
|
|
108
|
+
const dryRunInput = await prompt('Dry run (preview only)? (y/n) [n]: ');
|
|
109
|
+
dryRun = dryRunInput.toLowerCase() === 'y' || dryRunInput.toLowerCase() === 'yes';
|
|
110
|
+
|
|
111
|
+
console.log(''); // Blank line before proceeding
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Auto-detect version type from conventional commit format
|
|
115
|
+
if (!customVersion && !getFlag('--type', '-t')) {
|
|
116
|
+
if (message.startsWith('major:') || message.startsWith('MAJOR:')) {
|
|
117
|
+
type = 'major';
|
|
118
|
+
console.log('š Auto-detected: major version bump');
|
|
119
|
+
} else if (message.startsWith('minor:') || message.startsWith('MINOR:')) {
|
|
120
|
+
type = 'minor';
|
|
121
|
+
console.log('š Auto-detected: minor version bump');
|
|
122
|
+
} else if (message.startsWith('patch:') || message.startsWith('PATCH:')) {
|
|
123
|
+
type = 'patch';
|
|
124
|
+
console.log('š Auto-detected: patch version bump');
|
|
125
|
+
} else if (message.startsWith('feat:') || message.startsWith('feature:')) {
|
|
126
|
+
type = 'minor';
|
|
127
|
+
console.log('š Auto-detected: minor version bump (feature)');
|
|
128
|
+
} else if (message.startsWith('fix:')) {
|
|
129
|
+
type = 'patch';
|
|
130
|
+
console.log('š Auto-detected: patch version bump (fix)');
|
|
131
|
+
} else if (message.includes('BREAKING') || message.startsWith('breaking:')) {
|
|
132
|
+
type = 'major';
|
|
133
|
+
console.log('š Auto-detected: major version bump (breaking change)');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Validate version type
|
|
138
|
+
if (!customVersion && !['patch', 'minor', 'major'].includes(type)) {
|
|
139
|
+
console.error('Error: Version type must be patch, minor, or major');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// PRE-FLIGHT CHECKS
|
|
144
|
+
console.log('\nš Running pre-flight checks...\n');
|
|
145
|
+
|
|
146
|
+
// Check for uncommitted changes OR if user requested staging prompt
|
|
147
|
+
if ((config.requireCleanWorkingDir && !addMode) || promptForStaging) {
|
|
148
|
+
const status = execSync('git status --porcelain --untracked-files=no').toString().trim();
|
|
149
|
+
if (status || promptForStaging) {
|
|
150
|
+
// No files staged and changes exist - offer interactive selection
|
|
151
|
+
if (status) {
|
|
152
|
+
console.log('ā ļø You have uncommitted changes.\n');
|
|
153
|
+
}
|
|
154
|
+
console.log('š How would you like to stage files?\n');
|
|
155
|
+
console.log(' 1. Tracked files only (git add -u)');
|
|
156
|
+
console.log(' 2. All changes (git add -A)');
|
|
157
|
+
console.log(' 3. Interactive selection (git add -i)');
|
|
158
|
+
console.log(' 4. Patch mode (git add -p)');
|
|
159
|
+
console.log(' 5. Skip staging (continue without staging)\n');
|
|
160
|
+
|
|
161
|
+
const choice = await prompt('Select [1-5]: ');
|
|
162
|
+
|
|
163
|
+
if (choice === '1') {
|
|
164
|
+
addMode = 'tracked';
|
|
165
|
+
} else if (choice === '2') {
|
|
166
|
+
addMode = 'all';
|
|
167
|
+
} else if (choice === '3') {
|
|
168
|
+
addMode = 'interactive';
|
|
169
|
+
} else if (choice === '4') {
|
|
170
|
+
addMode = 'patch';
|
|
171
|
+
} else if (choice === '5') {
|
|
172
|
+
console.log('ā ļø Skipping file staging. Ensure files are staged manually.');
|
|
173
|
+
} else {
|
|
174
|
+
console.error('Invalid choice. Exiting.');
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
console.log('');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check current branch
|
|
182
|
+
const branch = execSync('git branch --show-current').toString().trim();
|
|
183
|
+
if (branch !== 'main' && branch !== 'master') {
|
|
184
|
+
console.log(`ā ļø Warning: You're on branch '${branch}', not main/master`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check if remote exists
|
|
188
|
+
try {
|
|
189
|
+
execSync('git remote get-url origin', {stdio: 'pipe'});
|
|
190
|
+
} catch {
|
|
191
|
+
if (push) {
|
|
192
|
+
console.error('ā Error: No remote repository configured, cannot push');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
console.log('ā ļø Warning: No remote repository configured');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log('ā
Pre-flight checks passed\n');
|
|
199
|
+
|
|
200
|
+
// DRY RUN MODE
|
|
201
|
+
if (dryRun) {
|
|
202
|
+
console.log('š¬ DRY RUN MODE - No changes will be made\n');
|
|
203
|
+
console.log('Would perform the following actions:');
|
|
204
|
+
|
|
205
|
+
if (addMode) {
|
|
206
|
+
const modeDescriptions = {
|
|
207
|
+
'tracked': 'Stage tracked files only (git add -u)',
|
|
208
|
+
'all': 'Stage all changes (git add -A)',
|
|
209
|
+
'interactive': 'Interactive selection (git add -i)',
|
|
210
|
+
'patch': 'Patch mode (git add -p)'
|
|
211
|
+
};
|
|
212
|
+
console.log(` 1. ${modeDescriptions[addMode]}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (customVersion) {
|
|
216
|
+
console.log(` 2. Set version to: ${customVersion}`);
|
|
217
|
+
} else {
|
|
218
|
+
console.log(` 2. Bump ${type} version`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(` 3. Commit with message: "${message}"`);
|
|
222
|
+
console.log(` 4. Create git tag with annotation`);
|
|
223
|
+
|
|
224
|
+
if (generateChangelog) {
|
|
225
|
+
console.log(' 5. Update CHANGELOG.md');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (generateReleaseNotes) {
|
|
229
|
+
console.log(' 6. Generate release notes file');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (push) {
|
|
233
|
+
console.log(' 7. Push to remote with tags');
|
|
234
|
+
} else {
|
|
235
|
+
console.log(' 7. (Skipping push - use --push to enable)');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log('\nā Dry run complete. Use without -d to apply changes.');
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// STAGE FILES if requested
|
|
243
|
+
if (addMode) {
|
|
244
|
+
console.log('š¦ Staging files...');
|
|
245
|
+
|
|
246
|
+
if (addMode === 'tracked') {
|
|
247
|
+
// Only stage tracked files that have been modified/deleted
|
|
248
|
+
execSync('git add -u', {stdio: 'inherit'});
|
|
249
|
+
} else if (addMode === 'all') {
|
|
250
|
+
// Stage all changes (respects .gitignore for new files)
|
|
251
|
+
execSync('git add -A', {stdio: 'inherit'});
|
|
252
|
+
} else if (addMode === 'interactive') {
|
|
253
|
+
// Interactive staging
|
|
254
|
+
execSync('git add -i', {stdio: 'inherit'});
|
|
255
|
+
} else if (addMode === 'patch') {
|
|
256
|
+
// Patch mode staging
|
|
257
|
+
execSync('git add -p', {stdio: 'inherit'});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// BUMP VERSION
|
|
261
|
+
console.log(`\nš¼ Bumping version...`);
|
|
262
|
+
|
|
263
|
+
// Get current version before bump
|
|
264
|
+
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
265
|
+
const oldVersion = packageJson.version;
|
|
266
|
+
|
|
267
|
+
// Always disable npm's git integration and handle it ourselves
|
|
268
|
+
if (customVersion) {
|
|
269
|
+
execSync(`npm version ${customVersion} --git-tag-version=false`, {stdio: 'inherit'});
|
|
270
|
+
} else {
|
|
271
|
+
execSync(`npm version ${type} --git-tag-version=false`, {stdio: 'inherit'});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Get new version
|
|
275
|
+
const newVersion = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
|
|
276
|
+
|
|
277
|
+
// Stage package files
|
|
278
|
+
execSync('git add package.json', {stdio: 'pipe'});
|
|
279
|
+
if (fs.existsSync('package-lock.json')) {
|
|
280
|
+
execSync('git add package-lock.json', {stdio: 'pipe'});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Commit with user's message
|
|
284
|
+
execSync(`git commit -m "${message}"`, {stdio: 'inherit'});
|
|
285
|
+
|
|
286
|
+
// Create annotated tag
|
|
287
|
+
execSync(`git tag -a v${newVersion} -m "Version ${newVersion}\n\n${message}"`, {stdio: 'pipe'});
|
|
288
|
+
|
|
289
|
+
// ADD GIT TAG ANNOTATION (keeping console.log for UX)
|
|
290
|
+
console.log('š·ļø Adding tag annotation...');
|
|
291
|
+
const tagMessage = `Version ${newVersion}\n\n${message}`;
|
|
292
|
+
execSync(`git tag -a v${newVersion} -f -m "${tagMessage}"`, {stdio: 'pipe'});
|
|
293
|
+
|
|
294
|
+
// GENERATE CHANGELOG
|
|
295
|
+
if (generateChangelog) {
|
|
296
|
+
console.log('š Updating CHANGELOG.md...');
|
|
297
|
+
const date = new Date().toISOString().split('T')[0];
|
|
298
|
+
const changelogEntry = `\n## [${newVersion}] - ${date}\n- ${message}\n`;
|
|
299
|
+
|
|
300
|
+
let changelog = '# Changelog\n';
|
|
301
|
+
if (fs.existsSync('CHANGELOG.md')) {
|
|
302
|
+
changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Insert new entry after the title
|
|
306
|
+
const lines = changelog.split('\n');
|
|
307
|
+
const titleIndex = lines.findIndex(line => line.startsWith('# Changelog'));
|
|
308
|
+
lines.splice(titleIndex + 1, 0, changelogEntry);
|
|
309
|
+
|
|
310
|
+
fs.writeFileSync('CHANGELOG.md', lines.join('\n'));
|
|
311
|
+
|
|
312
|
+
// Stage the changelog
|
|
313
|
+
execSync('git add CHANGELOG.md', {stdio: 'pipe'});
|
|
314
|
+
execSync(`git commit --amend --no-edit`, {stdio: 'pipe'});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// GENERATE RELEASE NOTES
|
|
318
|
+
if (generateReleaseNotes) {
|
|
319
|
+
console.log('š Generating release notes...');
|
|
320
|
+
const releaseNotes = `# Release v${newVersion}
|
|
321
|
+
|
|
322
|
+
Released: ${new Date().toISOString().split('T')[0]}
|
|
323
|
+
|
|
324
|
+
## Changes
|
|
325
|
+
${message}
|
|
326
|
+
|
|
327
|
+
## Installation
|
|
328
|
+
\`\`\`bash
|
|
329
|
+
npm install ${packageJson.name}@${newVersion}
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
## Full Changelog
|
|
333
|
+
See [CHANGELOG.md](./CHANGELOG.md) for complete version history.
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
const filename = `release-notes-v${newVersion}.md`;
|
|
337
|
+
fs.writeFileSync(filename, releaseNotes);
|
|
338
|
+
console.log(` Created: ${filename}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// PUSH TO REMOTE
|
|
342
|
+
if (push) {
|
|
343
|
+
console.log('š Pushing to remote...');
|
|
344
|
+
execSync('git push --follow-tags', {stdio: 'inherit'});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// STATS/SUMMARY
|
|
348
|
+
console.log('\nš Summary:');
|
|
349
|
+
console.log('ā'.repeat(50));
|
|
350
|
+
|
|
351
|
+
console.log(`\nš¦ Version: ${oldVersion} ā ${newVersion}`);
|
|
352
|
+
console.log(`š¬ Message: ${message}`);
|
|
353
|
+
console.log(`š·ļø Tag: v${newVersion}`);
|
|
354
|
+
console.log(`šæ Branch: ${branch}`);
|
|
355
|
+
|
|
356
|
+
if (generateChangelog) {
|
|
357
|
+
console.log(`š Changelog: Updated`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (generateReleaseNotes) {
|
|
361
|
+
console.log(`š Release notes: Generated`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (push) {
|
|
365
|
+
console.log(`š Remote: Pushed with tags`);
|
|
366
|
+
} else {
|
|
367
|
+
console.log(`š Remote: Not pushed (use --push to enable)`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Show files changed
|
|
371
|
+
console.log('\nš Files changed:');
|
|
372
|
+
const diff = execSync('git diff HEAD~1 --stat').toString();
|
|
373
|
+
console.log(diff);
|
|
374
|
+
|
|
375
|
+
console.log('ā'.repeat(50));
|
|
376
|
+
console.log('\nā
Version bump complete!\n');
|
|
377
|
+
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error('\nā Error:', error.message);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Show help
|
|
385
|
+
if (hasFlag('--help', '-h')) {
|
|
386
|
+
console.log(`
|
|
387
|
+
vnxt (vx) - Version Bump CLI Tool
|
|
388
|
+
|
|
389
|
+
Usage:
|
|
390
|
+
vnxt [options]
|
|
391
|
+
vx -m "commit message" [options]
|
|
392
|
+
|
|
393
|
+
Options:
|
|
394
|
+
-m, --message <msg> Commit message (required, or use interactive mode)
|
|
395
|
+
-t, --type <type> Version type: patch, minor, major (auto-detected from message)
|
|
396
|
+
-v, --version <ver> Set specific version (e.g., 2.0.0-beta.1)
|
|
397
|
+
-p, --push Push to remote with tags
|
|
398
|
+
-dnp, --no-push Prevent auto-push (overrides config)
|
|
399
|
+
-c, --changelog Update CHANGELOG.md
|
|
400
|
+
-d, --dry-run Show what would happen without making changes
|
|
401
|
+
-a, --all [mode] Stage files before versioning
|
|
402
|
+
Modes: tracked (default), all, interactive (i), patch (p)
|
|
403
|
+
If no mode specified, prompts interactively
|
|
404
|
+
-r, --release Generate release notes file
|
|
405
|
+
-h, --help Show this help message
|
|
406
|
+
|
|
407
|
+
Auto-detection:
|
|
408
|
+
- "major:" ā major version
|
|
409
|
+
- "minor:" ā minor version
|
|
410
|
+
- "patch:" ā patch version
|
|
411
|
+
- "feat:" or "feature:" ā minor version
|
|
412
|
+
- "fix:" ā patch version
|
|
413
|
+
- "BREAKING" or "breaking:" ā major version
|
|
414
|
+
|
|
415
|
+
Configuration:
|
|
416
|
+
Create .vnxtrc.json in your project:
|
|
417
|
+
{
|
|
418
|
+
"autoChangelog": true,
|
|
419
|
+
"defaultType": "patch",
|
|
420
|
+
"requireCleanWorkingDir": false,
|
|
421
|
+
"autoPush": true,
|
|
422
|
+
"defaultStageMode": "tracked",
|
|
423
|
+
"tagPrefix": "v"
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
Examples:
|
|
427
|
+
vx -m "fix: resolve bug" # Auto-pushes with autoPush: true
|
|
428
|
+
vx -m "feat: add new feature" # Auto-pushes with autoPush: true
|
|
429
|
+
vx -m "fix: bug" -dnp # Don't push (override)
|
|
430
|
+
vx -v 2.0.0-beta.1 -m "beta release"
|
|
431
|
+
vx -m "test" -d
|
|
432
|
+
vx -m "fix: bug" -a # Interactive prompt for staging
|
|
433
|
+
vx -m "fix: bug" -a tracked # Stage tracked files only
|
|
434
|
+
vx -m "fix: bug" -a all # Stage all changes
|
|
435
|
+
vx -m "fix: bug" -a i # Interactive git add
|
|
436
|
+
vx -m "fix: bug" -a p # Patch mode
|
|
437
|
+
vx # Interactive mode
|
|
438
|
+
`);
|
|
439
|
+
process.exit(0);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Run main function
|
|
443
|
+
main();
|