@versu/cli 0.4.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 ADDED
@@ -0,0 +1,386 @@
1
+ # @versu/cli - Command-Line Interface
2
+
3
+ Command-line interface for VERSU (Version Engine for Repo Semantic Evolution). This CLI provides all the power of VERSU's semantic versioning engine through an easy-to-use command-line tool, perfect for local development and custom CI/CD systems.
4
+
5
+ ## Installation
6
+
7
+ ### Global Installation
8
+
9
+ ```bash
10
+ npm install -g @versu/cli
11
+ ```
12
+
13
+ ### Local Installation
14
+
15
+ ```bash
16
+ npm install --save-dev @versu/cli
17
+ ```
18
+
19
+ ### Using npx (no installation)
20
+
21
+ ```bash
22
+ px @versu/cli
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic Usage
28
+
29
+ Navigate to your project root and run:
30
+
31
+ ```bash
32
+ versu
33
+ ```
34
+
35
+ This will:
36
+
37
+ 1. Detect your project type (Gradle, etc.)
38
+ 2. Analyze commits since the last version
39
+ 3. Calculate version bumps based on Conventional Commits
40
+ 4. Update version files
41
+ 5. Generate changelogs
42
+ 6. Create git commits and tags
43
+ 7. Push changes to remote
44
+
45
+ ### Dry Run
46
+
47
+ Preview what would happen without making changes:
48
+
49
+ ```bash
50
+ versu --dry-run
51
+ ```
52
+
53
+ ### Specify Project Root
54
+
55
+ ```bash
56
+ versu /path/to/project
57
+ ```
58
+
59
+ ### Specify Adapter
60
+
61
+ If auto-detection fails or you want to be explicit:
62
+
63
+ ```bash
64
+ versu --adapter gradle
65
+ ```
66
+
67
+ ## Command Reference
68
+
69
+ ### `versu [REPOSITORYROOT]`
70
+
71
+ Calculate and apply semantic version changes.
72
+
73
+ **Arguments:**
74
+
75
+ - `REPOSITORYROOT` - Path to the repository root (default: `.`)
76
+
77
+ **Flags:**
78
+
79
+ | Flag | Description | Default |
80
+ | ------ | ------------- | --------- |
81
+ | `--dry-run` | Run without writing or pushing changes | `false` |
82
+ | `--adapter <value>` | Language adapter (e.g., gradle). Auto-detected if not provided | - |
83
+ | `--push-tags` | Push tags to origin | `true` |
84
+ | `--no-push-tags` | Don't push tags to origin | - |
85
+ | `--prerelease-mode` | Generate pre-release versions instead of final versions | `false` |
86
+ | `--prerelease-id <value>` | Pre-release identifier (e.g., alpha, beta, rc) | `alpha` |
87
+ | `--bump-unchanged` | In prerelease mode, bump modules even when no changes are detected | `false` |
88
+ | `--add-build-metadata` | Add build metadata with short SHA to all versions | `false` |
89
+ | `--timestamp-versions` | Use timestamp-based prerelease identifiers (requires prerelease-mode) | `false` |
90
+ | `--append-snapshot` | Add -SNAPSHOT suffix to all versions if supported by adapter | `false` |
91
+ | `--push-changes` | Commit and push version changes and changelogs to remote | `true` |
92
+ | `--no-push-changes` | Don't commit and push changes | - |
93
+ | `--generate-changelog` | Generate or update changelog files for changed modules | `true` |
94
+ | `--no-generate-changelog` | Don't generate changelogs | - |
95
+ | `--output-file <value>` | Write calculated versions to a file in JSON format | - |
96
+
97
+ > 📖 **Detailed Pre-release Documentation**: See [@versu/core PRERELEASE.md](../core/PRERELEASE.md) for comprehensive examples and use cases.
98
+
99
+ ## Examples
100
+
101
+ ### Release Version
102
+
103
+ Apply semantic versions based on commits:
104
+
105
+ ```bash
106
+ versu
107
+ ```
108
+
109
+ ### Pre-release Versions
110
+
111
+ Generate beta pre-release versions:
112
+
113
+ ```bash
114
+ versu --prerelease-mode --prerelease-id beta
115
+ ```
116
+
117
+ ### Timestamp Versions
118
+
119
+ Generate timestamp-based pre-release versions for CI builds:
120
+
121
+ ```bash
122
+ versu --prerelease-mode --prerelease-id alpha --timestamp-versions --add-build-metadata
123
+ ```
124
+
125
+ This generates versions like: `1.2.3-alpha.20251208.1530+abc1234`
126
+
127
+ ### Gradle SNAPSHOT Versions
128
+
129
+ Generate Gradle SNAPSHOT versions:
130
+
131
+ ```bash
132
+ versu --append-snapshot
133
+ ```
134
+
135
+ ### Development Workflow
136
+
137
+ Bump all modules (even unchanged) for development:
138
+
139
+ ```bash
140
+ versu --prerelease-mode --bump-unchanged
141
+ ```
142
+
143
+ ### Local Testing
144
+
145
+ Test without committing or pushing:
146
+
147
+ ```bash
148
+ versu --dry-run --no-push-changes --no-push-tags
149
+ ```
150
+
151
+ ### Manual Git Operations
152
+
153
+ Calculate versions without automatic git operations:
154
+
155
+ ```bash
156
+ versu --no-push-changes --no-push-tags
157
+ ```
158
+
159
+ Then manually review, commit, and push.
160
+
161
+ ## Configuration
162
+
163
+ VERSU CLI uses the same configuration system as the core library. Configuration files are automatically detected in your repository root.
164
+
165
+ ### Supported Configuration Files
166
+
167
+ 1. `package.json` (in a `"versu"` property)
168
+ 2. `.versurc` (JSON or YAML)
169
+ 3. `.versurc.json`
170
+ 4. `.versurc.yaml` / `.versurc.yml`
171
+ 5. `.versurc.js` (JavaScript)
172
+ 6. `versu.config.js` (JavaScript)
173
+
174
+ ### Configuration Example
175
+
176
+ `.versurc.json`:
177
+
178
+ ```json
179
+ {
180
+ "defaultBump": "patch",
181
+ "commitTypes": {
182
+ "feat": "minor",
183
+ "fix": "patch",
184
+ "perf": "patch",
185
+ "refactor": "patch",
186
+ "docs": "ignore",
187
+ "test": "ignore",
188
+ "chore": "ignore"
189
+ },
190
+ "dependencyRules": {
191
+ "onMajorOfDependency": "minor",
192
+ "onMinorOfDependency": "patch",
193
+ "onPatchOfDependency": "none"
194
+ }
195
+ }
196
+ ```
197
+
198
+ For more configuration examples, see the [core package documentation](../core).
199
+
200
+ ## Gradle Project Support
201
+
202
+ The CLI supports Gradle projects with:
203
+
204
+ - **Multi-module projects** via `settings.gradle(.kts)`
205
+ - **Version management** through root `gradle.properties` file
206
+ - **Dependency detection** via custom Gradle init script
207
+ - **Both DSLs**: Groovy and Kotlin
208
+
209
+ ### Example Project Structure
210
+
211
+ ```text
212
+ myproject/
213
+ ├── settings.gradle.kts
214
+ ├── build.gradle.kts
215
+ ├── gradle.properties # All module versions defined here
216
+ ├── core/
217
+ │ └── build.gradle.kts
218
+ └── api/
219
+ └── build.gradle.kts
220
+ ```
221
+
222
+ ### Example `gradle.properties`
223
+
224
+ ```properties
225
+ # Root module version
226
+ version=1.0.0
227
+
228
+ # Submodule versions
229
+ core.version=2.1.0
230
+ api.version=1.5.0
231
+ ```
232
+
233
+ ## Commit Message Format
234
+
235
+ VERSU uses [Conventional Commits](https://conventionalcommits.org/) to determine version bumps:
236
+
237
+ ```text
238
+ <type>[optional scope]: <description>
239
+
240
+ [optional body]
241
+
242
+ [optional footer(s)]
243
+ ```
244
+
245
+ **Examples:**
246
+
247
+ - `feat(api): add new endpoint` → **minor** bump
248
+ - `fix(core): resolve memory leak` → **patch** bump
249
+ - `feat!: breaking API change` → **major** bump
250
+
251
+ ### Breaking Changes
252
+
253
+ Breaking changes trigger **major** version bumps:
254
+
255
+ 1. Using `!` after the type: `feat!: remove deprecated API`
256
+ 2. Using `BREAKING CHANGE:` in the footer:
257
+
258
+ ```text
259
+ feat: update API
260
+
261
+ BREAKING CHANGE: The old API is removed
262
+ ```
263
+
264
+ ## CI/CD Integration
265
+
266
+ ### GitHub Actions
267
+
268
+ ```yaml
269
+ - name: Install VERSU CLI
270
+ run: npm install -g @versu/cli
271
+
272
+ - name: Version modules
273
+ run: versu --adapter gradle
274
+ ```
275
+
276
+ ### GitLab CI
277
+
278
+ ```yaml
279
+ version:
280
+ script:
281
+ - npm install -g @versu/cli
282
+ - versu --adapter gradle
283
+ ```
284
+
285
+ ### Jenkins
286
+
287
+ ```groovy
288
+ stage('Version') {
289
+ steps {
290
+ sh 'npm install -g @versu/cli'
291
+ sh 'versu --adapter gradle'
292
+ }
293
+ }
294
+ ```
295
+
296
+ ### Local Development
297
+
298
+ Add to your `package.json`:
299
+
300
+ ```json
301
+ {
302
+ "scripts": {
303
+ "version": "versu --dry-run",
304
+ "version:release": "versu"
305
+ }
306
+ }
307
+ ```
308
+
309
+ Then run:
310
+
311
+ ```bash
312
+ npm run version # Dry run
313
+ npm run version:release # Actual release
314
+ ```
315
+
316
+ ## Troubleshooting
317
+
318
+ ### Command Not Found
319
+
320
+ If `versu` is not found after global installation:
321
+
322
+ 1. Check npm global bin path: `npm bin -g`
323
+ 2. Ensure it's in your PATH
324
+ 3. Try using npx: `npx @versu/cli`
325
+
326
+ ### Permission Denied on Push
327
+
328
+ If you get permission errors when pushing:
329
+
330
+ 1. Ensure you have proper git credentials configured
331
+ 2. Check if you have write access to the repository
332
+ 3. Use `--no-push-changes --no-push-tags` to skip git operations
333
+
334
+ ### No Version Bump Detected
335
+
336
+ If versions aren't bumping:
337
+
338
+ 1. Check commit messages follow Conventional Commits format
339
+ 2. Verify you have commits since the last version
340
+ 3. Check configuration if certain commit types are ignored
341
+ 4. Use `--dry-run` to see what VERSU detects
342
+
343
+ ### Adapter Not Detected
344
+
345
+ If auto-detection fails:
346
+
347
+ 1. Verify your project has the expected build files
348
+ 2. Explicitly specify the adapter: `--adapter gradle`
349
+ 3. Check if your project structure is supported
350
+
351
+ ## Development
352
+
353
+ ### Building from Source
354
+
355
+ ```bash
356
+ # From monorepo root
357
+ npm install
358
+ npm run build
359
+
360
+ # Or from CLI package
361
+ cd packages/cli
362
+ npm install
363
+ npm run build
364
+ ```
365
+
366
+ ### Running Locally
367
+
368
+ ```bash
369
+ # After building
370
+ node dist/index.js --dry-run
371
+ ```
372
+
373
+ ### Publishing
374
+
375
+ ```bash
376
+ npm publish --workspace packages/cli --access public
377
+ ```
378
+
379
+ ## Related Packages
380
+
381
+ - **[@versu/core](../core)** - Core library for custom integrations
382
+ - **[@versu/action](../action)** - GitHub Actions integration
383
+
384
+ ## License
385
+
386
+ MIT License - see [LICENSE](../../LICENSE) for details.
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({dir: import.meta.url})
@@ -0,0 +1,27 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class Version extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ repositoryRoot: import("@oclif/core/interfaces").Arg<string, {
7
+ exists?: boolean;
8
+ }>;
9
+ };
10
+ static flags: {
11
+ version: import("@oclif/core/interfaces").BooleanFlag<void>;
12
+ "dry-run": import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ adapter: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ "push-tags": import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ "prerelease-mode": import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ "prerelease-id": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ "bump-unchanged": import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ "add-build-metadata": import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ "timestamp-versions": import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ "append-snapshot": import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
+ "push-changes": import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ "generate-changelog": import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
+ "output-file": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
24
+ };
25
+ run(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAS,MAAM,aAAa,CAAC;AAInD,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,OAAO;IAC1C,OAAgB,WAAW,SAAkD;IAE7E,OAAgB,QAAQ,WAKtB;IAEF,MAAM,CAAC,IAAI;;;;MAMT;IAEF,OAAgB,KAAK;;;;;;;;;;;;;;MAyDnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA8B3B"}
package/dist/index.js ADDED
@@ -0,0 +1,100 @@
1
+ import { Args, Command, Flags } from "@oclif/core";
2
+ import { VersuRunner, initLogger } from "@versu/core";
3
+ import { OclifLogger } from "./logger.js";
4
+ export default class Version extends Command {
5
+ static description = "Calculate and apply semantic version changes";
6
+ static examples = [
7
+ "<%= config.bin %> <%= command.id %>",
8
+ "<%= config.bin %> <%= command.id %> --adapter gradle",
9
+ "<%= config.bin %> <%= command.id %> --dry-run",
10
+ "<%= config.bin %> <%= command.id %> --prerelease-mode --prerelease-id alpha",
11
+ ];
12
+ static args = {
13
+ repositoryRoot: Args.directory({
14
+ required: false,
15
+ description: "Path to the repository root",
16
+ default: ".",
17
+ }),
18
+ };
19
+ static flags = {
20
+ version: Flags.version({ char: 'v' }),
21
+ "dry-run": Flags.boolean({
22
+ description: "Run without writing or pushing changes",
23
+ default: false,
24
+ }),
25
+ adapter: Flags.string({
26
+ description: "Language adapter (e.g., gradle). Auto-detected if not provided",
27
+ required: false,
28
+ }),
29
+ "push-tags": Flags.boolean({
30
+ description: "Push tags to origin",
31
+ default: true,
32
+ allowNo: true,
33
+ }),
34
+ "prerelease-mode": Flags.boolean({
35
+ description: "Generate pre-release versions instead of final versions",
36
+ default: false,
37
+ }),
38
+ "prerelease-id": Flags.string({
39
+ description: "Pre-release identifier (e.g., alpha, beta, rc)",
40
+ default: "alpha",
41
+ }),
42
+ "bump-unchanged": Flags.boolean({
43
+ description: "In prerelease mode, bump modules even when no changes are detected",
44
+ default: false,
45
+ }),
46
+ "add-build-metadata": Flags.boolean({
47
+ description: "Add build metadata with short SHA to all versions",
48
+ default: false,
49
+ }),
50
+ "timestamp-versions": Flags.boolean({
51
+ description: "Use timestamp-based prerelease identifiers (requires prerelease-mode)",
52
+ default: false,
53
+ }),
54
+ "append-snapshot": Flags.boolean({
55
+ description: "Add -SNAPSHOT suffix to all versions if supported by adapter",
56
+ default: false,
57
+ }),
58
+ "push-changes": Flags.boolean({
59
+ description: "Commit and push version changes and changelogs to remote",
60
+ default: true,
61
+ allowNo: true,
62
+ }),
63
+ "generate-changelog": Flags.boolean({
64
+ description: "Generate or update changelog files for changed modules",
65
+ default: true,
66
+ allowNo: true,
67
+ }),
68
+ "output-file": Flags.string({
69
+ description: "Path to the output file for project information",
70
+ default: "project-information.json",
71
+ }),
72
+ };
73
+ async run() {
74
+ const { flags, args } = await this.parse(Version);
75
+ initLogger(new OclifLogger(this));
76
+ try {
77
+ const options = {
78
+ repoRoot: args.repositoryRoot,
79
+ adapter: flags.adapter,
80
+ dryRun: flags["dry-run"],
81
+ pushTags: flags["push-tags"],
82
+ prereleaseMode: flags["prerelease-mode"],
83
+ prereleaseId: flags["prerelease-id"],
84
+ bumpUnchanged: flags["bump-unchanged"],
85
+ addBuildMetadata: flags["add-build-metadata"],
86
+ timestampVersions: flags["timestamp-versions"],
87
+ appendSnapshot: flags["append-snapshot"],
88
+ pushChanges: flags["push-changes"],
89
+ generateChangelog: flags["generate-changelog"],
90
+ outputFile: flags["output-file"],
91
+ };
92
+ const runner = new VersuRunner(options);
93
+ await runner.run();
94
+ }
95
+ catch (error) {
96
+ const errorMessage = error instanceof Error ? error.message : String(error);
97
+ this.error(`❌ Command failed: ${errorMessage}`);
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { Logger } from '@versu/core';
3
+ export declare class OclifLogger implements Logger {
4
+ private readonly cmd;
5
+ constructor(cmd: Command);
6
+ info(message: string): void;
7
+ warning(message: string | Error): void;
8
+ error(message: string | Error, context?: Record<string, unknown>): void;
9
+ debug(message: string): void;
10
+ }
11
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAK1C,qBAAa,WAAY,YAAW,MAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,OAAO;IACzC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAG3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAGtC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAGvE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAG/B"}
package/dist/logger.js ADDED
@@ -0,0 +1,20 @@
1
+ import Debug from 'debug';
2
+ const debug = Debug('versu');
3
+ export class OclifLogger {
4
+ cmd;
5
+ constructor(cmd) {
6
+ this.cmd = cmd;
7
+ }
8
+ info(message) {
9
+ this.cmd.log(message);
10
+ }
11
+ warning(message) {
12
+ this.cmd.warn(message);
13
+ }
14
+ error(message, context) {
15
+ this.cmd.error(message, context);
16
+ }
17
+ debug(message) {
18
+ debug(message);
19
+ }
20
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@versu/cli",
3
+ "version": "0.4.0",
4
+ "description": "Version Engine for Repo Semantic Evolution (CLI)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "versu": "./bin/run.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc --project tsconfig.json --outDir dist",
12
+ "test": "vitest",
13
+ "test:coverage": "vitest --coverage",
14
+ "lint": "eslint src/**/*.ts",
15
+ "format": "prettier --write src/**/*.ts"
16
+ },
17
+ "keywords": [
18
+ "versu",
19
+ "cli",
20
+ "semantic-versioning",
21
+ "conventional-commits",
22
+ "version-engine",
23
+ "monorepo"
24
+ ],
25
+ "author": "tvcsantos",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/tvcsantos/versu.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/tvcsantos/versu/issues"
33
+ },
34
+ "homepage": "https://github.com/tvcsantos/versu#readme",
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "types": "dist/index.d.ts",
39
+ "dependencies": {
40
+ "@oclif/core": "^4.8.0",
41
+ "@versu/core": "^0.4.0",
42
+ "oclif": "^4.14.0"
43
+ },
44
+ "devDependencies": {
45
+ "@oclif/dev-cli": "^1.26.10",
46
+ "@types/debug": "^4.1.12",
47
+ "@types/node": "^20.19.23",
48
+ "@typescript-eslint/eslint-plugin": "^6.15.0",
49
+ "@typescript-eslint/parser": "^6.15.0",
50
+ "eslint": "^8.56.0",
51
+ "prettier": "^3.1.1",
52
+ "ts-node": "^10.9.2",
53
+ "ts-node-dev": "^2.0.0",
54
+ "typescript": "^5.3.3",
55
+ "vitest": "^1.1.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=20"
59
+ },
60
+ "oclif": {
61
+ "bin": "versu",
62
+ "dirname": "versu",
63
+ "topicSeparator": " ",
64
+ "plugins": [],
65
+ "commands": {
66
+ "strategy": "single",
67
+ "target": "./dist/index.js"
68
+ }
69
+ }
70
+ }