release-suite 2.0.0 → 3.0.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.
Files changed (39) hide show
  1. package/.github/workflows/create-and-publish.yml +243 -0
  2. package/CHANGELOG.md +19 -32
  3. package/README.md +60 -47
  4. package/bin/changelog.js +88 -0
  5. package/bin/dry-run.js +54 -0
  6. package/bin/release-notes.js +79 -0
  7. package/bin/tag.js +73 -0
  8. package/bin/{compute-version.js → version.js} +40 -33
  9. package/docs/api.md +25 -11
  10. package/docs/changelog.md +266 -0
  11. package/docs/ci.md +154 -112
  12. package/docs/config.md +139 -0
  13. package/docs/dry-run.md +229 -0
  14. package/docs/release-notes.md +149 -0
  15. package/docs/release-process.md +167 -0
  16. package/docs/tag.md +193 -0
  17. package/docs/{compute-version.md → version.md} +37 -67
  18. package/eslint.config.js +1 -1
  19. package/lib/changelog/generate.js +145 -0
  20. package/lib/{changelog.js → changelog/helpers.js} +54 -154
  21. package/lib/changelog/rebuild.js +85 -0
  22. package/lib/commits.js +15 -5
  23. package/lib/config.js +51 -0
  24. package/lib/dry-run.js +79 -0
  25. package/lib/git.js +17 -0
  26. package/lib/{release-notes.js → release-notes/generate.js} +37 -28
  27. package/lib/tag/create.js +91 -0
  28. package/lib/utils.js +57 -8
  29. package/lib/{compute-version.js → version/compute.js} +10 -3
  30. package/package.json +12 -14
  31. package/release.config.js +10 -0
  32. package/.github/workflows/create-release-pr.yml +0 -101
  33. package/.github/workflows/publish-on-merge.yml +0 -95
  34. package/bin/create-tag.js +0 -110
  35. package/bin/generate-changelog.js +0 -66
  36. package/bin/generate-release-notes.js +0 -70
  37. package/bin/preview.js +0 -75
  38. package/docs/generate-changelog.md +0 -107
  39. package/docs/generate-release-notes.md +0 -111
@@ -0,0 +1,243 @@
1
+ name: Create & Publish Release
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ id-token: write # 🔐 REQUIRED for OIDC
11
+ contents: write
12
+ pull-requests: write
13
+ packages: write
14
+
15
+ concurrency:
16
+ group: release-main
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ # ------------------------------------------------------------
21
+ # JOB 1 — CREATE RELEASE PR + TAG DECISION
22
+ # ------------------------------------------------------------
23
+ create:
24
+ runs-on: ubuntu-latest
25
+ timeout-minutes: 15
26
+ env:
27
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28
+
29
+ # Job-level outputs = contract with publish job
30
+ outputs:
31
+ should_publish: ${{ steps.compute.outputs.should_publish }}
32
+ version: ${{ steps.compute.outputs.version }}
33
+ tagPrefix: ${{ steps.compute.outputs.tagPrefix }}
34
+ tag: ${{ steps.tag.outputs.tag }}
35
+
36
+ # Only run when:
37
+ # - manual dispatch OR
38
+ # - PR merged into main AND it's NOT a release PR
39
+ if: >
40
+ github.event_name == 'workflow_dispatch' ||
41
+ (
42
+ github.event.pull_request.merged == true &&
43
+ !contains(join(github.event.pull_request.labels.*.name, ','), 'release')
44
+ )
45
+
46
+ steps:
47
+ - name: Checkout
48
+ uses: actions/checkout@v4
49
+ with:
50
+ fetch-depth: 0
51
+ persist-credentials: true
52
+
53
+ - name: Setup Node
54
+ uses: actions/setup-node@v4
55
+ with:
56
+ node-version: 24
57
+
58
+ - name: Install dependencies
59
+ run: npm ci
60
+
61
+ - name: Install release-suite (self)
62
+ run: npm install .
63
+
64
+ # --------------------------------------------------------
65
+ # Decide if a release should happen
66
+ # --------------------------------------------------------
67
+ - name: Compute next version
68
+ id: compute
69
+ run: |
70
+ set +e
71
+ RESULT=$(node bin/version.js compute)
72
+ STATUS=$?
73
+ VERSION=$(echo "$RESULT" | jq -r '.nextVersion // empty')
74
+ TAG_PREFIX=$(echo "$RESULT" | jq -r '.tagPrefix // ""')
75
+
76
+ echo "$RESULT"
77
+
78
+ if [ "$STATUS" -ne 0 ] || [ -z "$VERSION" ]; then
79
+ echo "should_publish=false" >> $GITHUB_OUTPUT
80
+ else
81
+ echo "should_publish=true" >> $GITHUB_OUTPUT
82
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
83
+ echo "tagPrefix=$TAG_PREFIX" >> $GITHUB_OUTPUT
84
+ fi
85
+
86
+ # --------------------------------------------------------
87
+ # Prepare release artifacts
88
+ # --------------------------------------------------------
89
+ - name: Bump package.json
90
+ if: steps.compute.outputs.should_publish == 'true'
91
+ run: npm version ${{ steps.compute.outputs.version }} --no-git-tag-version
92
+
93
+ - name: Sync package-lock.json
94
+ if: steps.compute.outputs.should_publish == 'true'
95
+ run: npm install --package-lock-only
96
+
97
+ - name: Build
98
+ run: npm run build --if-present
99
+
100
+ - name: Generate changelog
101
+ if: steps.compute.outputs.should_publish == 'true'
102
+ run: node bin/changelog.js generate
103
+
104
+ # --------------------------------------------------------
105
+ # Stage files (dist is optional)
106
+ # --------------------------------------------------------
107
+ - name: Stage release files
108
+ if: steps.compute.outputs.should_publish == 'true'
109
+ run: |
110
+ git add package.json package-lock.json CHANGELOG.md
111
+ [ -d dist ] && git add dist || true
112
+ git status --short
113
+
114
+ # --------------------------------------------------------
115
+ # Create Release PR
116
+ # --------------------------------------------------------
117
+ - name: Create Release PR
118
+ id: pr
119
+ if: steps.compute.outputs.should_publish == 'true'
120
+ uses: peter-evans/create-pull-request@v6
121
+ with:
122
+ token: ${{ secrets.GITHUB_TOKEN }}
123
+ branch: release/${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }}
124
+ title: ":bricks: chore(release): ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }} [skip ci]"
125
+ commit-message: ":bricks: chore(release): prepare version ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }} [skip ci]"
126
+ labels: release
127
+ delete-branch: true
128
+ assignees: ${{ github.event.pull_request.merged_by.login }}
129
+ body: |
130
+ Automated release PR.
131
+
132
+ - Version bump: ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }}
133
+ - Updated changelog
134
+ - Build artifacts included if present
135
+
136
+ Merging this PR will trigger publish automatically.
137
+
138
+ # --------------------------------------------------------
139
+ # Merge Release PR (best-effort)
140
+ # --------------------------------------------------------
141
+ - name: Merge Release PR
142
+ if: steps.pr.outputs.pull-request-number != ''
143
+ run: |
144
+ PR=${{ steps.pr.outputs.pull-request-number }}
145
+ echo "Merging PR #$PR"
146
+ if ! gh pr merge "$PR" --squash --admin; then
147
+ echo "Immediate merge failed, attempting to enable auto-merge"
148
+ gh pr merge "$PR" --auto --squash
149
+ fi
150
+
151
+ # --------------------------------------------------------
152
+ # Validate merge (soft)
153
+ # --------------------------------------------------------
154
+ - name: Validate release commit
155
+ id: validate
156
+ run: |
157
+ git fetch origin main --depth=1
158
+ git checkout main
159
+ git reset --hard origin/main
160
+
161
+ if git log -1 --pretty=%s | grep -q "^:bricks: chore(release):"; then
162
+ echo "ok=true" >> $GITHUB_OUTPUT
163
+ else
164
+ echo "ok=false" >> $GITHUB_OUTPUT
165
+ fi
166
+
167
+ # --------------------------------------------------------
168
+ # Create Git tag (soft)
169
+ # --------------------------------------------------------
170
+ - name: Create tag
171
+ id: tag
172
+ if: steps.validate.outputs.ok == 'true'
173
+ run: |
174
+ set +e
175
+ RESULT=$(node bin/tag.js create)
176
+ TAG=$(echo "$RESULT" | jq -r '.tag // empty')
177
+
178
+ echo "$RESULT"
179
+
180
+ if [ -n "$TAG" ]; then
181
+ echo "tag=$TAG" >> $GITHUB_OUTPUT
182
+ fi
183
+
184
+ # ------------------------------------------------------------
185
+ # JOB 2 — PUBLISH (only if tag exists)
186
+ # ------------------------------------------------------------
187
+ publish:
188
+ needs: create
189
+ runs-on: ubuntu-latest
190
+ timeout-minutes: 15
191
+ env:
192
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
193
+
194
+ if: needs.create.outputs.tag != ''
195
+
196
+ steps:
197
+ - name: Checkout
198
+ uses: actions/checkout@v4
199
+ with:
200
+ fetch-depth: 0
201
+ persist-credentials: true
202
+
203
+ - name: Setup Node (npm OIDC)
204
+ uses: actions/setup-node@v4
205
+ with:
206
+ node-version: 24
207
+ registry-url: https://registry.npmjs.org
208
+ always-auth: true
209
+
210
+ - name: Install dependencies
211
+ run: npm ci
212
+
213
+ - name: Install release-suite (self)
214
+ run: npm install .
215
+
216
+ # --------------------------------------------------------
217
+ # Publish to npm using Trusted Publishing (OIDC)
218
+ # --------------------------------------------------------
219
+ - name: Publish to npm (Trusted Publishing)
220
+ run: npm publish
221
+
222
+ # --------------------------------------------------------
223
+ # Generate release notes for GitHub Release
224
+ # --------------------------------------------------------
225
+ - name: Generate GitHub Release Notes
226
+ run: node bin/release-notes.js generate
227
+
228
+ # --------------------------------------------------------
229
+ # Create GitHub Release with notes and attach built assets
230
+ # --------------------------------------------------------
231
+ - name: Create GitHub Release
232
+ env:
233
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
234
+ run: |
235
+ ASSETS=()
236
+ if [ -d dist ]; then
237
+ ASSETS=(dist/**)
238
+ fi
239
+
240
+ gh release create "${{ needs.create.outputs.tag }}" \
241
+ --title "${{ needs.create.outputs.tag }}" \
242
+ --notes-file RELEASE_NOTES.md \
243
+ "${ASSETS[@]}"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ All notable changes to this project will be documented in this file.
4
+ The format follows the conventions of [Conventional Commits](https://www.conventionalcommits.org) and semantic versioning (SemVer).
5
+
6
+ ## 3.0.0
7
+
8
+ ### 💥 Breaking Changes
9
+ - Introduce action-based CLI and reorganize release architecture (#21)
10
+
11
+ ### 📚 Documentation
12
+ - Update README.md and ci.md with GitHub CLI usage (#20)
13
+
14
+ ### 🤖 CI
15
+ - Simplify GitHub CLI usage and make authentication explicit (#19)
16
+ - Unify release automation into a single resilient workflow (#18)
17
+
3
18
  ## 2.0.0
4
19
 
5
20
  ### 💥 Breaking Changes
@@ -11,52 +26,24 @@
11
26
  ## 1.0.1
12
27
 
13
28
  ### 🐛 Fixes
14
-
15
29
  - Harden changelog generation for squash commits (#13)
16
30
 
17
- ### 📚 Docs
18
-
19
- - Update ci/cd examples with trigger adjustment
20
-
21
- ### 🔁 CI
22
-
23
- - Adjust trigger in workflow
31
+ ### 🤖 CI
32
+ - Harden workflow triggers and merge conditions (#12)
24
33
 
25
34
  ## 1.0.0
26
35
 
27
36
  ### 💥 Breaking Changes
28
-
29
37
  - Harden computeVersion contract and CLI behavior (#4)
30
38
 
31
39
  ### 🐛 Fixes
32
-
33
40
  - Normalize path resolution for all CLI scripts (#2)
34
41
 
35
- ### 🔧 Chore
36
-
37
- -
38
- -
39
- -
40
- -
41
- -
42
- -
43
- -
44
- -
45
- -
46
- -
47
- -
48
- -
49
- -
50
- -
51
- -
52
-
53
- ### 🔁 CI
54
-
42
+ ### 🤖 CI
55
43
  - Harden local script execution and dist resolution (#10)
56
44
  - Binary resolution (#3)
57
45
 
58
46
  ## 0.1.0
59
47
 
60
- ### 🔧 Chore
61
-
48
+ ### 📦 Other
62
49
  - Init: project created
package/README.md CHANGED
@@ -9,8 +9,8 @@ Semantic versioning tools for Git-based projects, providing automated version co
9
9
  - Automatic version bump based on commit messages
10
10
  - Conventional commit parsing (custom prefixes supported)
11
11
  - Auto-generated `CHANGELOG.md`
12
- - Auto-generated `RELEASE_NOTES.md` using GitHub CLI (gh)
13
- - Local preview mode (`CHANGELOG.preview.md`, `RELEASE_NOTES.preview.md`)
12
+ - Auto-generated `RELEASE_NOTES.md` using GitHub API
13
+ - Local dry-run mode (`CHANGELOG.dry-run.md`, `RELEASE_NOTES.dry-run.md`)
14
14
  - CI/CD ready for GitHub Actions
15
15
  - No commit rules enforced on the main project
16
16
  - Trusted Publishing (OIDC) — no npm tokens required
@@ -26,78 +26,94 @@ Add to your project's `package.json`:
26
26
  ```json
27
27
  {
28
28
  "scripts": {
29
- "preview": "rs-preview create",
30
- "preview:clean": "rs-preview remove",
31
- "compute-version": "rs-compute-version",
32
- "compute-version:ci": "rs-compute-version --ci --json",
33
- "compute-version:json": "rs-compute-version --json",
34
- "changelog": "rs-generate-changelog",
35
- "release-notes": "rs-generate-release-notes"
29
+ "dry-run": "rs-dry-run create",
30
+ "dry-run:clean": "rs-dry-run remove",
31
+ "version:compute": "rs-version compute",
32
+ "changelog": "rs-changelog generate",
33
+ "release-notes": "rs-release-notes generate",
34
+ "tag:create": "rs-tag create"
36
35
  }
37
36
  }
38
37
  ```
39
38
 
40
- Generate preview files without touching your real changelog:
39
+ Generate dry-run files without touching your real changelog:
41
40
 
42
41
  ```bash
43
- npm run preview
42
+ npm run dry-run
44
43
  ```
45
44
 
46
- Remove previews:
45
+ Remove dry-run files:
47
46
 
48
47
  ```bash
49
- npm run preview:clear
48
+ npm run dry-run:clear
50
49
  ```
51
50
 
51
+ ## ⚙️ Configuration
52
+
53
+ Release Suite can be configured using a `release.config.js` file.
54
+
55
+ This file controls:
56
+
57
+ - Git tag prefix (`v1.2.3` vs `1.2.3`)
58
+ - Emoji usage in changelog rendering
59
+
60
+ See [`docs/config.md`](docs/config.md) for full documentation.
61
+
62
+ ---
63
+
52
64
  ## 🖥️ CLI Commands
53
65
 
54
- | Command | Description |
55
- | --------------------------- | --------------------------------------------------------- |
56
- | `rs-compute-version` | Computes next semantic version based on git commits |
57
- | `rs-generate-changelog` | Generates `CHANGELOG.md` |
58
- | `rs-generate-release-notes` | Generates `RELEASE_NOTES.md` using GitHub PRs |
59
- | `rs-preview` | Generates preview changelog & release notes |
60
- | `rs-create-tag` | Create and push a git tag based on `package.json` version |
66
+ | Command | Description |
67
+ | --------------------------- | ---------------------------------------------------------- |
68
+ | `rs-version compute` | Computes next semantic version based on git commits |
69
+ | `rs-changelog generate` | Generates a new changelog entry for the next release |
70
+ | `rs-changelog rebuild` ⚠️ | Fully rebuilds CHANGELOG.md from git history (Danger Zone) |
71
+ | `rs-release-notes generate` | Generates RELEASE_NOTES.md |
72
+ | `rs-dry-run` | Generates dry-run changelog & release notes |
73
+ | `rs-tag create` | Creates and pushes a git tag |
61
74
 
62
75
  Each command follows a strict and predictable CLI contract (exit codes, stdout, JSON mode).
63
76
 
64
77
  > 💡 **Note about execution**
65
78
  >
79
+ > ⚠️ `rs-changelog rebuild` is a destructive operation.
80
+ > Always use `--dry-run` first.
81
+ >
66
82
  > - When using these commands via `npm run`, they can be referenced directly (`rs-*`).
67
83
  > - In CI/CD environments (e.g. GitHub Actions), always invoke them using `npx`
68
- > (e.g. `npx rs-generate-changelog`) to ensure proper binary resolution.
84
+ > (e.g. `npx rs-changelog generate`) to ensure proper binary resolution.
69
85
 
70
86
  ## 🔁 Release Flow
71
87
 
72
- This project follows a **two-step release strategy** designed for safety,
73
- automation and reusability.
88
+ This project uses an automated, PR-based release strategy
89
+ designed for safety and traceability.
74
90
 
75
- ### 1️⃣ Prepare Release (Create Release PR)
91
+ See [Release Process](docs/release-process.md) for details.
92
+
93
+ ### 1️⃣ Create Release PR
76
94
 
77
95
  Triggered when:
78
96
 
79
97
  - A PR is merged into `main`
98
+ - Or the workflow is manually dispatched
80
99
 
81
100
  Actions:
82
101
 
83
102
  - Computes next semantic version
84
- - Updates `package.json`
85
- - Generates `CHANGELOG.md`
103
+ - Updates `package.json` and `CHANGELOG.md`
86
104
  - Builds the project (if applicable)
87
- - Opens a **Release PR** (`release/x.y.z`)
105
+ - Opens and auto-merges a Release PR
106
+ - Creates a Git tag
88
107
 
89
108
  ### 2️⃣ Publish Release
90
109
 
91
- Triggered when:
92
-
93
- - A Release PR (`release/x.y.z`) with `release` label is merged into `main`
110
+ Triggered automatically after the Release PR merge (only if tag exists).
94
111
 
95
112
  Actions:
96
113
 
97
- - Creates a Git tag
98
114
  - Publishes to npm using **Trusted Publishing (OIDC)**
99
115
  - Generates GitHub Release Notes
100
- - Uploads build artifacts (`dist/**`)
116
+ - Uploads build artifacts (`dist/**` if present)
101
117
 
102
118
  ---
103
119
 
@@ -105,10 +121,13 @@ Actions:
105
121
 
106
122
  ```mermaid
107
123
  flowchart TD
108
- A[Feature / Fix PR] -->|Merge| B[main]
109
- B -->|create-release-pr.yml| C[Create Release PR]
110
- C -->|Manual Review & Merge| D[pull_request.closed]
111
- D -->|publish-on-merge.yml| E[Publish Release]
124
+ A[Feature / Fix PR] -->|merge| B[main]
125
+ B -->|workflow trigger| C[Compute Version]
126
+ C -->|release needed| D[Create Release PR]
127
+ D -->|auto-merge| E[main updated]
128
+ E -->|create tag| F[Git Tag]
129
+ F -->|publish| G[npm Registry]
130
+ F -->|release| H[GitHub Release]
112
131
  ```
113
132
 
114
133
  ✔️ Fully automated releases
@@ -123,12 +142,6 @@ flowchart TD
123
142
 
124
143
  This project is designed to be used in automated pipelines.
125
144
 
126
- Typical flow:
127
-
128
- 1. Create a Release PR (compute version, changelog, build)
129
- 2. Review and merge the Release PR into `main`
130
- 3. Publish the release (tag, npm, GitHub Release)
131
-
132
145
  👉 See full examples in [`docs/ci.md`](./docs/ci.md)
133
146
 
134
147
  ## 📦 Publishing to npm (Trusted Publishing)
@@ -154,11 +167,11 @@ are **not available via npm or npx**, since they are not installed as a dependen
154
167
  In this case, run the scripts directly with Node.js:
155
168
 
156
169
  ```bash
157
- node bin/compute-version.js
158
- node bin/generate-changelog.js
159
- node bin/generate-release-notes.js
160
- node bin/preview.js create
161
- node bin/create-tag.js
170
+ node bin/version.js compute
171
+ node bin/changelog.js generate
172
+ node bin/release-notes.js generate
173
+ node bin/dry-run.js create
174
+ node bin/tag.js create
162
175
  ```
163
176
 
164
177
  To test the CLI as a real consumer, you can use:
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { generateChangelog } from "../lib/changelog/generate.js";
4
+ import { rebuildChangelog } from "../lib/changelog/rebuild.js";
5
+ import { parseFlags } from "../lib/utils.js";
6
+
7
+ /* ===========================
8
+ * CLI
9
+ * =========================== */
10
+
11
+ /**
12
+ * Main CLI entrypoint for changelog (generate/rebuild).
13
+ *
14
+ * Behavior:
15
+ * - Calls generateChangelog({ dryRun: flags.dryRun }).
16
+ * - Calls rebuildChangelog({ dryRun: flags.dryRun }).
17
+ *
18
+ * Exit codes (contract):
19
+ * - 0 -> changelog generated/rebuilt
20
+ * - 10 -> no release/no-tags
21
+ * - 2 -> no-commits/no-valid-commits
22
+ * - 11 -> already exists
23
+ * - 1 -> unexpected error
24
+ */
25
+ function main() {
26
+ const action = process.argv[2];
27
+ const flags = parseFlags(process.argv.slice(3), {
28
+ dryRun: "--dry-run",
29
+ });
30
+
31
+ if (!["generate", "rebuild"].includes(action)) {
32
+ console.error(
33
+ JSON.stringify(
34
+ {
35
+ "error": "invalid-usage",
36
+ "message": "Invalid action. Usage: npx rs-changelog [generate|rebuild]"
37
+ },
38
+ null,
39
+ 2
40
+ )
41
+ );
42
+ process.exit(1);
43
+ }
44
+
45
+ try {
46
+
47
+ let result = null;
48
+ if (action === "generate") {
49
+ result = generateChangelog({ dryRun: flags.dryRun });
50
+ }
51
+ else if (action === "rebuild") {
52
+ result = rebuildChangelog({ dryRun: flags.dryRun });
53
+ }
54
+
55
+ console.log(JSON.stringify(result, null, 2));
56
+
57
+ if (action === "generate") {
58
+ if (result.generated) process.exit(0);
59
+ if (result.reason === "no-release") process.exit(10);
60
+ if (result.reason === "no-commits") process.exit(2);
61
+ if (result.reason === "already-exists") process.exit(11);
62
+ }
63
+ else if (action === "rebuild") {
64
+ if (result.rebuilt) process.exit(0);
65
+ if (result.reason === "no-tags") process.exit(10);
66
+ if (result.reason === "no-valid-commits") process.exit(2);
67
+ }
68
+
69
+ process.exit(1);
70
+ } catch (err) {
71
+ console.error(
72
+ JSON.stringify(
73
+ {
74
+ error: "unexpected-error",
75
+ message: err.message || String(err),
76
+ },
77
+ null,
78
+ 2
79
+ )
80
+ );
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ const __filename = fileURLToPath(import.meta.url);
86
+ if (process.argv[1] === __filename) {
87
+ main();
88
+ }
package/bin/dry-run.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { dryRun } from "../lib/dry-run.js";
4
+
5
+ /* ===========================
6
+ * CLI
7
+ * =========================== */
8
+
9
+ function main() {
10
+ const action = process.argv[2];
11
+
12
+ if (!["create", "remove"].includes(action)) {
13
+ console.error(
14
+ JSON.stringify(
15
+ {
16
+ "error": "invalid-usage",
17
+ "message": "Invalid action. Usage: npx rs-dry-run [create|remove]"
18
+ },
19
+ null,
20
+ 2
21
+ )
22
+ );
23
+ process.exit(1);
24
+ }
25
+
26
+ try {
27
+ const result = dryRun({ action });
28
+
29
+ console.log(JSON.stringify(result, null, 2));
30
+
31
+ if (action === "create" && result.generated === false) {
32
+ process.exit(10);
33
+ }
34
+
35
+ process.exit(0);
36
+ } catch (err) {
37
+ console.error(
38
+ JSON.stringify(
39
+ {
40
+ error: "unexpected-error",
41
+ message: err.message || String(err),
42
+ },
43
+ null,
44
+ 2
45
+ )
46
+ );
47
+ process.exit(1);
48
+ }
49
+ }
50
+
51
+ const __filename = fileURLToPath(import.meta.url);
52
+ if (process.argv[1] === __filename) {
53
+ main();
54
+ }