zenith-language 0.3.0 ā 0.4.1
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/.github/workflows/release.yml +254 -0
- package/.releaserc.json +73 -0
- package/CHANGELOG.md +44 -0
- package/README.md +1 -1
- package/package.json +105 -97
- package/scripts/release.ts +554 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Zenith Automated Release Workflow
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# This workflow handles automated releases for all Zenith repositories.
|
|
5
|
+
#
|
|
6
|
+
# TRIGGERS:
|
|
7
|
+
# - Push to 'main' branch (analyzes commits for version bump)
|
|
8
|
+
# - Manual trigger via workflow_dispatch (with optional dry-run mode)
|
|
9
|
+
# - Tag creation (v*) for explicit version releases
|
|
10
|
+
#
|
|
11
|
+
# FEATURES:
|
|
12
|
+
# - Conventional Commits parsing for automatic version determination
|
|
13
|
+
# - Automatic CHANGELOG.md generation
|
|
14
|
+
# - GitHub Release creation
|
|
15
|
+
# - Optional NPM publishing
|
|
16
|
+
# - Commits updated files back to repo
|
|
17
|
+
# - Dry-run mode for testing
|
|
18
|
+
# - Monorepo support (detects changed packages)
|
|
19
|
+
#
|
|
20
|
+
# REQUIRED SECRETS:
|
|
21
|
+
# - NPM_TOKEN: For publishing to NPM (if enabled)
|
|
22
|
+
# - GITHUB_TOKEN: Automatically provided by GitHub Actions
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
name: Release
|
|
26
|
+
|
|
27
|
+
on:
|
|
28
|
+
push:
|
|
29
|
+
branches:
|
|
30
|
+
- main
|
|
31
|
+
tags:
|
|
32
|
+
- 'v*'
|
|
33
|
+
paths-ignore:
|
|
34
|
+
- '**.md'
|
|
35
|
+
- '.github/**'
|
|
36
|
+
- '!.github/workflows/release.yml'
|
|
37
|
+
|
|
38
|
+
workflow_dispatch:
|
|
39
|
+
inputs:
|
|
40
|
+
dry_run:
|
|
41
|
+
description: 'Dry run mode (no actual release)'
|
|
42
|
+
required: false
|
|
43
|
+
default: false
|
|
44
|
+
type: boolean
|
|
45
|
+
package:
|
|
46
|
+
description: 'Specific package to release (for monorepo, leave empty for auto-detect)'
|
|
47
|
+
required: false
|
|
48
|
+
default: ''
|
|
49
|
+
type: string
|
|
50
|
+
bump_type:
|
|
51
|
+
description: 'Force version bump type (leave empty for auto-detect from commits)'
|
|
52
|
+
required: false
|
|
53
|
+
default: ''
|
|
54
|
+
type: choice
|
|
55
|
+
options:
|
|
56
|
+
- ''
|
|
57
|
+
- patch
|
|
58
|
+
- minor
|
|
59
|
+
- major
|
|
60
|
+
publish_npm:
|
|
61
|
+
description: 'Publish to NPM'
|
|
62
|
+
required: false
|
|
63
|
+
default: true
|
|
64
|
+
type: boolean
|
|
65
|
+
|
|
66
|
+
# Prevent concurrent releases
|
|
67
|
+
concurrency:
|
|
68
|
+
group: release-${{ github.ref }}
|
|
69
|
+
cancel-in-progress: false
|
|
70
|
+
|
|
71
|
+
env:
|
|
72
|
+
BUN_VERSION: '1.1.38'
|
|
73
|
+
|
|
74
|
+
jobs:
|
|
75
|
+
# ==========================================================================
|
|
76
|
+
# Detect Changes (for monorepo support)
|
|
77
|
+
# ==========================================================================
|
|
78
|
+
detect-changes:
|
|
79
|
+
name: Detect Changed Packages
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
outputs:
|
|
82
|
+
packages: ${{ steps.detect.outputs.packages }}
|
|
83
|
+
has_changes: ${{ steps.detect.outputs.has_changes }}
|
|
84
|
+
steps:
|
|
85
|
+
- name: Checkout Repository
|
|
86
|
+
uses: actions/checkout@v4
|
|
87
|
+
with:
|
|
88
|
+
fetch-depth: 0
|
|
89
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
90
|
+
|
|
91
|
+
- name: Setup Bun
|
|
92
|
+
uses: oven-sh/setup-bun@v2
|
|
93
|
+
with:
|
|
94
|
+
bun-version: ${{ env.BUN_VERSION }}
|
|
95
|
+
|
|
96
|
+
- name: Detect Changed Packages
|
|
97
|
+
id: detect
|
|
98
|
+
run: |
|
|
99
|
+
# First, check if this is a single-package repo (package.json in root)
|
|
100
|
+
if [ -f "./package.json" ]; then
|
|
101
|
+
# Count subdirectories with package.json (excluding node_modules)
|
|
102
|
+
SUB_PACKAGES=$(find . -mindepth 2 -name "package.json" -not -path "*/node_modules/*" | wc -l)
|
|
103
|
+
|
|
104
|
+
if [ "$SUB_PACKAGES" -eq 0 ]; then
|
|
105
|
+
# Single package repo - always release from root
|
|
106
|
+
echo "Single package repository detected"
|
|
107
|
+
echo "packages=[\".\"]" >> $GITHUB_OUTPUT
|
|
108
|
+
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
109
|
+
exit 0
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Monorepo detection
|
|
114
|
+
PACKAGES=$(find . -name "package.json" -not -path "*/node_modules/*" -not -path "*/.git/*" | xargs -I {} dirname {} | sed 's|^\./||' | grep -v "^$" | sort -u)
|
|
115
|
+
|
|
116
|
+
# For monorepos, detect which packages changed
|
|
117
|
+
CHANGED_PACKAGES="[]"
|
|
118
|
+
if [ "${{ github.event.inputs.package }}" != "" ]; then
|
|
119
|
+
CHANGED_PACKAGES="[\"${{ github.event.inputs.package }}\"]"
|
|
120
|
+
else
|
|
121
|
+
# Get changed files since last tag or in the current push
|
|
122
|
+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
123
|
+
if [ -z "$LAST_TAG" ]; then
|
|
124
|
+
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git ls-files)
|
|
125
|
+
else
|
|
126
|
+
CHANGED_FILES=$(git diff --name-only $LAST_TAG HEAD)
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Match changed files to packages
|
|
130
|
+
CHANGED_PKGS=""
|
|
131
|
+
for pkg in $PACKAGES; do
|
|
132
|
+
if echo "$CHANGED_FILES" | grep -q "^$pkg/"; then
|
|
133
|
+
if [ -z "$CHANGED_PKGS" ]; then
|
|
134
|
+
CHANGED_PKGS="\"$pkg\""
|
|
135
|
+
else
|
|
136
|
+
CHANGED_PKGS="$CHANGED_PKGS, \"$pkg\""
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
done
|
|
140
|
+
CHANGED_PACKAGES="[$CHANGED_PKGS]"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
echo "packages=$CHANGED_PACKAGES" >> $GITHUB_OUTPUT
|
|
144
|
+
if [ "$CHANGED_PACKAGES" = "[]" ]; then
|
|
145
|
+
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
146
|
+
else
|
|
147
|
+
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ==========================================================================
|
|
152
|
+
# Release Job
|
|
153
|
+
# ==========================================================================
|
|
154
|
+
release:
|
|
155
|
+
name: Release
|
|
156
|
+
needs: detect-changes
|
|
157
|
+
if: needs.detect-changes.outputs.has_changes == 'true'
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
permissions:
|
|
160
|
+
contents: write
|
|
161
|
+
packages: write
|
|
162
|
+
|
|
163
|
+
strategy:
|
|
164
|
+
fail-fast: false
|
|
165
|
+
matrix:
|
|
166
|
+
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
|
|
167
|
+
|
|
168
|
+
steps:
|
|
169
|
+
- name: Checkout Repository
|
|
170
|
+
uses: actions/checkout@v4
|
|
171
|
+
with:
|
|
172
|
+
fetch-depth: 0
|
|
173
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
174
|
+
|
|
175
|
+
- name: Setup Bun
|
|
176
|
+
uses: oven-sh/setup-bun@v2
|
|
177
|
+
with:
|
|
178
|
+
bun-version: ${{ env.BUN_VERSION }}
|
|
179
|
+
|
|
180
|
+
- name: Configure Git
|
|
181
|
+
run: |
|
|
182
|
+
git config user.name "github-actions[bot]"
|
|
183
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
184
|
+
|
|
185
|
+
- name: Install Dependencies
|
|
186
|
+
working-directory: ${{ matrix.package }}
|
|
187
|
+
run: bun install
|
|
188
|
+
|
|
189
|
+
- name: Run Release Script
|
|
190
|
+
id: release
|
|
191
|
+
working-directory: ${{ matrix.package }}
|
|
192
|
+
env:
|
|
193
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
194
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
195
|
+
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
|
|
196
|
+
BUMP_TYPE: ${{ github.event.inputs.bump_type || '' }}
|
|
197
|
+
PUBLISH_NPM: ${{ github.event.inputs.publish_npm || 'true' }}
|
|
198
|
+
run: |
|
|
199
|
+
# Run the Bun release script
|
|
200
|
+
bun run scripts/release.ts
|
|
201
|
+
|
|
202
|
+
- name: Build Package
|
|
203
|
+
if: steps.release.outputs.should_release == 'true'
|
|
204
|
+
working-directory: ${{ matrix.package }}
|
|
205
|
+
run: |
|
|
206
|
+
if bun run build 2>/dev/null; then
|
|
207
|
+
echo "Build completed successfully"
|
|
208
|
+
else
|
|
209
|
+
echo "No build script found or build not required"
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
- name: Commit Changes
|
|
213
|
+
if: steps.release.outputs.should_release == 'true' && github.event.inputs.dry_run != 'true'
|
|
214
|
+
working-directory: ${{ matrix.package }}
|
|
215
|
+
run: |
|
|
216
|
+
git add CHANGELOG.md package.json
|
|
217
|
+
git commit -m "chore(release): v${{ steps.release.outputs.new_version }} [skip ci]" || echo "No changes to commit"
|
|
218
|
+
git push
|
|
219
|
+
|
|
220
|
+
- name: Create GitHub Release
|
|
221
|
+
if: steps.release.outputs.should_release == 'true' && github.event.inputs.dry_run != 'true'
|
|
222
|
+
uses: softprops/action-gh-release@v2
|
|
223
|
+
with:
|
|
224
|
+
tag_name: v${{ steps.release.outputs.new_version }}
|
|
225
|
+
name: Release v${{ steps.release.outputs.new_version }}
|
|
226
|
+
body_path: ${{ matrix.package }}/RELEASE_NOTES.md
|
|
227
|
+
draft: false
|
|
228
|
+
prerelease: false
|
|
229
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
230
|
+
|
|
231
|
+
- name: Publish to NPM
|
|
232
|
+
if: steps.release.outputs.should_release == 'true' && github.event.inputs.dry_run != 'true' && (github.event.inputs.publish_npm == 'true' || github.event.inputs.publish_npm == '')
|
|
233
|
+
working-directory: ${{ matrix.package }}
|
|
234
|
+
run: |
|
|
235
|
+
# Check if package is not private
|
|
236
|
+
PRIVATE=$(cat package.json | bun -e "console.log(JSON.parse(await Bun.stdin.text()).private || false)")
|
|
237
|
+
if [ "$PRIVATE" = "false" ]; then
|
|
238
|
+
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
|
239
|
+
bun publish --access public || npm publish --access public
|
|
240
|
+
else
|
|
241
|
+
echo "Package is private, skipping NPM publish"
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
- name: Summary
|
|
245
|
+
run: |
|
|
246
|
+
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
|
|
247
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
248
|
+
if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then
|
|
249
|
+
echo "ā ļø **DRY RUN MODE** - No actual release was created" >> $GITHUB_STEP_SUMMARY
|
|
250
|
+
fi
|
|
251
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
252
|
+
echo "- **Package**: ${{ matrix.package }}" >> $GITHUB_STEP_SUMMARY
|
|
253
|
+
echo "- **Version**: ${{ steps.release.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
|
254
|
+
echo "- **Bump Type**: ${{ steps.release.outputs.bump_type }}" >> $GITHUB_STEP_SUMMARY
|
package/.releaserc.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"types": {
|
|
4
|
+
"feat": {
|
|
5
|
+
"title": "⨠Features",
|
|
6
|
+
"bump": "minor",
|
|
7
|
+
"description": "New features or functionality"
|
|
8
|
+
},
|
|
9
|
+
"fix": {
|
|
10
|
+
"title": "š Bug Fixes",
|
|
11
|
+
"bump": "patch",
|
|
12
|
+
"description": "Bug fixes and corrections"
|
|
13
|
+
},
|
|
14
|
+
"perf": {
|
|
15
|
+
"title": "ā” Performance Improvements",
|
|
16
|
+
"bump": "patch",
|
|
17
|
+
"description": "Performance optimizations"
|
|
18
|
+
},
|
|
19
|
+
"refactor": {
|
|
20
|
+
"title": "ā»ļø Code Refactoring",
|
|
21
|
+
"bump": "patch",
|
|
22
|
+
"description": "Code changes that neither fix bugs nor add features"
|
|
23
|
+
},
|
|
24
|
+
"docs": {
|
|
25
|
+
"title": "š Documentation",
|
|
26
|
+
"bump": null,
|
|
27
|
+
"description": "Documentation only changes"
|
|
28
|
+
},
|
|
29
|
+
"style": {
|
|
30
|
+
"title": "š Styles",
|
|
31
|
+
"bump": null,
|
|
32
|
+
"description": "Code style changes (formatting, whitespace)"
|
|
33
|
+
},
|
|
34
|
+
"test": {
|
|
35
|
+
"title": "ā
Tests",
|
|
36
|
+
"bump": null,
|
|
37
|
+
"description": "Adding or updating tests"
|
|
38
|
+
},
|
|
39
|
+
"build": {
|
|
40
|
+
"title": "š¦ Build System",
|
|
41
|
+
"bump": "patch",
|
|
42
|
+
"description": "Build system or dependency changes"
|
|
43
|
+
},
|
|
44
|
+
"ci": {
|
|
45
|
+
"title": "š§ CI Configuration",
|
|
46
|
+
"bump": null,
|
|
47
|
+
"description": "CI/CD configuration changes"
|
|
48
|
+
},
|
|
49
|
+
"chore": {
|
|
50
|
+
"title": "šØ Chores",
|
|
51
|
+
"bump": null,
|
|
52
|
+
"description": "Maintenance tasks and other changes"
|
|
53
|
+
},
|
|
54
|
+
"revert": {
|
|
55
|
+
"title": "āŖ Reverts",
|
|
56
|
+
"bump": "patch",
|
|
57
|
+
"description": "Reverting previous commits"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"skipCI": [
|
|
61
|
+
"[skip ci]",
|
|
62
|
+
"[ci skip]",
|
|
63
|
+
"[no ci]",
|
|
64
|
+
"chore(release)"
|
|
65
|
+
],
|
|
66
|
+
"tagPrefix": "v",
|
|
67
|
+
"branches": {
|
|
68
|
+
"main": "latest",
|
|
69
|
+
"next": "next",
|
|
70
|
+
"beta": "beta",
|
|
71
|
+
"alpha": "alpha"
|
|
72
|
+
}
|
|
73
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### ⨠Features
|
|
11
|
+
|
|
12
|
+
- **language**: update syntax grammar for new directives and reactive bindings (cb65c98)
|
|
13
|
+
|
|
14
|
+
### š Bug Fixes
|
|
15
|
+
|
|
16
|
+
- **release**: use appendFileSync for GitHub Actions output (c953f51)
|
|
17
|
+
|
|
18
|
+
### š Other Changes
|
|
19
|
+
|
|
20
|
+
-
|
|
21
|
+
c1aa285dac910ec64f2240c849ff6c0d18b7cd2e ()
|
|
22
|
+
-
|
|
23
|
+
dc90df4092e298ffdce221f8127ae87b0aeed45c ()
|
|
24
|
+
-
|
|
25
|
+
18982c541782091455f32bb5c354e66a06c2938a ()
|
|
26
|
+
-
|
|
27
|
+
5a5046880d2afbc7df70abce0062c6a4be21859e ()
|
|
28
|
+
-
|
|
29
|
+
8627c68faf8cd28521675a6216dc7462c7deb2b2 ()
|
|
30
|
+
-
|
|
31
|
+
e06fdd9e3167f30671c559e98c3fb75088c7e1b6 ()
|
|
32
|
+
- 0.2.9 (de391df)
|
|
33
|
+
-
|
|
34
|
+
e0ad1cd02292af51ac321e7580ae9e534abd6c1b ()
|
|
35
|
+
-
|
|
36
|
+
e4ca2b1d2af81e1cf9876b0932cd3178b7d1bf7e ()
|
|
37
|
+
-
|
|
38
|
+
cff8202737008d97c6527703f51783583eae7e6f ()
|
|
39
|
+
-
|
|
40
|
+
645d159ba240aec6cb6cab5bf332743f6eed4fcd ()
|
|
41
|
+
-
|
|
42
|
+
52507461378cb8f2d87245b84924790a191879ad ()
|
|
43
|
+
- ()
|
|
44
|
+
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ VS Code extension providing world-class development support for the Zenith frame
|
|
|
12
12
|
- **IntelliSense**: Smart completions for Zenith components, hooks, and reactive state.
|
|
13
13
|
- **Emmet Support**: Accelerated HTML development inside `.zen` templates.
|
|
14
14
|
- **Project Scaffolding**: Integrated support for starting new projects.
|
|
15
|
-
- **LSP Integration**: Leverages `@
|
|
15
|
+
- **LSP Integration**: Leverages `@zenithbuild/language-server` for powerful diagnostics and refactoring.
|
|
16
16
|
|
|
17
17
|
## Supported Extensions
|
|
18
18
|
|
package/package.json
CHANGED
|
@@ -1,102 +1,110 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
],
|
|
50
|
-
"configuration": "./language-configuration.json"
|
|
51
|
-
}
|
|
2
|
+
"name": "zenith-language",
|
|
3
|
+
"displayName": "Zenith Language Support",
|
|
4
|
+
"description": "Syntax highlighting, IntelliSense, and editor support for Zenith Framework (.zen files)",
|
|
5
|
+
"version": "0.4.1",
|
|
6
|
+
"publisher": "ZenithBuild",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.80.0"
|
|
9
|
+
},
|
|
10
|
+
"main": "./out/extension.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build:server": "cd ../zenith-language-server && bun run build",
|
|
13
|
+
"compile": "bun x esbuild src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node && cp ../zenith-language-server/dist/server.js out/server.js",
|
|
14
|
+
"watch": "bun run compile -- --watch",
|
|
15
|
+
"build:marketplace": "bun run build:server && bun run compile && node scripts/build.js marketplace",
|
|
16
|
+
"build:openvsx": "bun run build:server && bun run compile && node scripts/build.js openvsx",
|
|
17
|
+
"build:all": "bun run build:server && bun run compile && node scripts/build.js all",
|
|
18
|
+
"release": "bun run scripts/release.ts",
|
|
19
|
+
"release:dry": "bun run scripts/release.ts --dry-run",
|
|
20
|
+
"release:patch": "bun run scripts/release.ts --bump=patch",
|
|
21
|
+
"release:minor": "bun run scripts/release.ts --bump=minor",
|
|
22
|
+
"release:major": "bun run scripts/release.ts --bump=major"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"@types/vscode": "^1.80.0",
|
|
27
|
+
"esbuild": "^0.19.0",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
},
|
|
30
|
+
"categories": [
|
|
31
|
+
"Programming Languages"
|
|
32
|
+
],
|
|
33
|
+
"icon": "assets/logo.png",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"zenith",
|
|
36
|
+
"zen",
|
|
37
|
+
"syntax",
|
|
38
|
+
"highlighting",
|
|
39
|
+
"intellisense",
|
|
40
|
+
"framework"
|
|
41
|
+
],
|
|
42
|
+
"contributes": {
|
|
43
|
+
"languages": [
|
|
44
|
+
{
|
|
45
|
+
"id": "zenith",
|
|
46
|
+
"aliases": [
|
|
47
|
+
"Zenith",
|
|
48
|
+
"zenith"
|
|
52
49
|
],
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"path": "./syntaxes/zenith.tmLanguage.json",
|
|
58
|
-
"embeddedLanguages": {
|
|
59
|
-
"source.js": "javascript",
|
|
60
|
-
"source.ts": "typescript",
|
|
61
|
-
"source.css": "css",
|
|
62
|
-
"text.html.basic": "html",
|
|
63
|
-
"meta.embedded.block.javascript": "javascript",
|
|
64
|
-
"meta.embedded.block.typescript": "typescript",
|
|
65
|
-
"meta.embedded.block.css": "css"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
50
|
+
"extensions": [
|
|
51
|
+
".zen",
|
|
52
|
+
".zen.html",
|
|
53
|
+
".zenx"
|
|
68
54
|
],
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"emmet.syntaxProfiles": {
|
|
86
|
-
"zenith": "html"
|
|
87
|
-
}
|
|
55
|
+
"configuration": "./language-configuration.json"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"grammars": [
|
|
59
|
+
{
|
|
60
|
+
"language": "zenith",
|
|
61
|
+
"scopeName": "text.html.zenith",
|
|
62
|
+
"path": "./syntaxes/zenith.tmLanguage.json",
|
|
63
|
+
"embeddedLanguages": {
|
|
64
|
+
"source.js": "javascript",
|
|
65
|
+
"source.ts": "typescript",
|
|
66
|
+
"source.css": "css",
|
|
67
|
+
"text.html.basic": "html",
|
|
68
|
+
"meta.embedded.block.javascript": "javascript",
|
|
69
|
+
"meta.embedded.block.typescript": "typescript",
|
|
70
|
+
"meta.embedded.block.css": "css"
|
|
88
71
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"configurationDefaults": {
|
|
75
|
+
"[zenith]": {
|
|
76
|
+
"editor.formatOnSave": true,
|
|
77
|
+
"editor.wordBasedSuggestions": "off",
|
|
78
|
+
"editor.suggest.insertMode": "replace",
|
|
79
|
+
"editor.semanticHighlighting.enabled": true,
|
|
80
|
+
"editor.quickSuggestions": {
|
|
81
|
+
"other": true,
|
|
82
|
+
"comments": false,
|
|
83
|
+
"strings": true
|
|
84
|
+
},
|
|
85
|
+
"editor.autoClosingBrackets": "always"
|
|
86
|
+
},
|
|
87
|
+
"emmet.includeLanguages": {
|
|
88
|
+
"zenith": "html"
|
|
89
|
+
},
|
|
90
|
+
"emmet.syntaxProfiles": {
|
|
91
|
+
"zenith": "html"
|
|
92
|
+
}
|
|
101
93
|
}
|
|
102
|
-
}
|
|
94
|
+
},
|
|
95
|
+
"repository": {
|
|
96
|
+
"type": "git",
|
|
97
|
+
"url": "https://github.com/zenithbuild/zenith"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://github.com/zenithbuild/zenith#readme",
|
|
100
|
+
"bugs": {
|
|
101
|
+
"url": "https://github.com/zenithbuild/zenith/issues"
|
|
102
|
+
},
|
|
103
|
+
"publishConfig": {
|
|
104
|
+
"access": "public"
|
|
105
|
+
},
|
|
106
|
+
"license": "MIT",
|
|
107
|
+
"dependencies": {
|
|
108
|
+
"vscode-languageclient": "^9.0.1"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* =============================================================================
|
|
4
|
+
* Zenith Release Script
|
|
5
|
+
* =============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Automated release script using Bun for all Zenith repositories.
|
|
8
|
+
* Handles:
|
|
9
|
+
* - Conventional Commit parsing for automatic version determination
|
|
10
|
+
* - CHANGELOG.md generation
|
|
11
|
+
* - package.json version updates
|
|
12
|
+
* - Release notes generation for GitHub releases
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* bun run scripts/release.ts # Normal release
|
|
16
|
+
* bun run scripts/release.ts --dry-run # Test without making changes
|
|
17
|
+
* bun run scripts/release.ts --bump=major # Force major version bump
|
|
18
|
+
* bun run scripts/release.ts --bump=minor # Force minor version bump
|
|
19
|
+
* bun run scripts/release.ts --bump=patch # Force patch version bump
|
|
20
|
+
*
|
|
21
|
+
* Environment Variables:
|
|
22
|
+
* DRY_RUN - Set to 'true' for dry run mode
|
|
23
|
+
* BUMP_TYPE - Force bump type (patch, minor, major)
|
|
24
|
+
* GITHUB_TOKEN - GitHub token for API calls (optional)
|
|
25
|
+
*
|
|
26
|
+
* =============================================================================
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { $ } from "bun";
|
|
30
|
+
import { existsSync } from "fs";
|
|
31
|
+
import { join } from "path";
|
|
32
|
+
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Types
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
interface Commit {
|
|
38
|
+
hash: string;
|
|
39
|
+
type: string;
|
|
40
|
+
scope: string | null;
|
|
41
|
+
subject: string;
|
|
42
|
+
body: string;
|
|
43
|
+
breaking: boolean;
|
|
44
|
+
raw: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface PackageJson {
|
|
48
|
+
name: string;
|
|
49
|
+
version: string;
|
|
50
|
+
private?: boolean;
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface ReleaseConfig {
|
|
55
|
+
types: {
|
|
56
|
+
[key: string]: {
|
|
57
|
+
title: string;
|
|
58
|
+
bump: "patch" | "minor" | "major" | null;
|
|
59
|
+
hidden?: boolean;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
skipCI: string[];
|
|
63
|
+
tagPrefix: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
type BumpType = "patch" | "minor" | "major";
|
|
67
|
+
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Configuration
|
|
70
|
+
// =============================================================================
|
|
71
|
+
|
|
72
|
+
const DEFAULT_CONFIG: ReleaseConfig = {
|
|
73
|
+
types: {
|
|
74
|
+
feat: { title: "⨠Features", bump: "minor" },
|
|
75
|
+
fix: { title: "š Bug Fixes", bump: "patch" },
|
|
76
|
+
perf: { title: "ā” Performance Improvements", bump: "patch" },
|
|
77
|
+
refactor: { title: "ā»ļø Code Refactoring", bump: "patch" },
|
|
78
|
+
docs: { title: "š Documentation", bump: null },
|
|
79
|
+
style: { title: "š Styles", bump: null },
|
|
80
|
+
test: { title: "ā
Tests", bump: null },
|
|
81
|
+
build: { title: "š¦ Build System", bump: "patch" },
|
|
82
|
+
ci: { title: "š§ CI Configuration", bump: null },
|
|
83
|
+
chore: { title: "šØ Chores", bump: null },
|
|
84
|
+
revert: { title: "āŖ Reverts", bump: "patch" },
|
|
85
|
+
},
|
|
86
|
+
skipCI: ["[skip ci]", "[ci skip]", "[no ci]"],
|
|
87
|
+
tagPrefix: "v",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// Utility Functions
|
|
92
|
+
// =============================================================================
|
|
93
|
+
|
|
94
|
+
function log(message: string, type: "info" | "success" | "warn" | "error" = "info"): void {
|
|
95
|
+
const colors = {
|
|
96
|
+
info: "\x1b[36m", // Cyan
|
|
97
|
+
success: "\x1b[32m", // Green
|
|
98
|
+
warn: "\x1b[33m", // Yellow
|
|
99
|
+
error: "\x1b[31m", // Red
|
|
100
|
+
};
|
|
101
|
+
const reset = "\x1b[0m";
|
|
102
|
+
const prefix = {
|
|
103
|
+
info: "ā¹",
|
|
104
|
+
success: "ā",
|
|
105
|
+
warn: "ā ",
|
|
106
|
+
error: "ā",
|
|
107
|
+
};
|
|
108
|
+
console.log(`${colors[type]}${prefix[type]}${reset} ${message}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseArgs(): { dryRun: boolean; bumpType: BumpType | null } {
|
|
112
|
+
const args = process.argv.slice(2);
|
|
113
|
+
let dryRun = process.env.DRY_RUN === "true";
|
|
114
|
+
let bumpType: BumpType | null = (process.env.BUMP_TYPE as BumpType) || null;
|
|
115
|
+
|
|
116
|
+
for (const arg of args) {
|
|
117
|
+
if (arg === "--dry-run") {
|
|
118
|
+
dryRun = true;
|
|
119
|
+
} else if (arg.startsWith("--bump=")) {
|
|
120
|
+
const bump = arg.split("=")[1] as BumpType;
|
|
121
|
+
if (["patch", "minor", "major"].includes(bump)) {
|
|
122
|
+
bumpType = bump;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { dryRun, bumpType };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// Git Functions
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
async function getLastTag(): Promise<string | null> {
|
|
135
|
+
try {
|
|
136
|
+
const result = await $`git describe --tags --abbrev=0 2>/dev/null`.text();
|
|
137
|
+
return result.trim() || null;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function tagExists(version: string): Promise<boolean> {
|
|
144
|
+
try {
|
|
145
|
+
const result = await $`git tag -l "v${version}"`.text();
|
|
146
|
+
return result.trim() !== "";
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
async function getCommitsSinceTag(tag: string | null): Promise<string[]> {
|
|
154
|
+
try {
|
|
155
|
+
let result: string;
|
|
156
|
+
if (tag) {
|
|
157
|
+
result = await $`git log ${tag}..HEAD --pretty=format:"%H|%s|%b|||"`.text();
|
|
158
|
+
} else {
|
|
159
|
+
result = await $`git log --pretty=format:"%H|%s|%b|||"`.text();
|
|
160
|
+
}
|
|
161
|
+
return result.split("|||").filter((c) => c.trim());
|
|
162
|
+
} catch {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function parseCommit(rawCommit: string, config: ReleaseConfig): Commit | null {
|
|
168
|
+
const parts = rawCommit.trim().split("|");
|
|
169
|
+
if (parts.length < 2) return null;
|
|
170
|
+
|
|
171
|
+
const hash = parts[0];
|
|
172
|
+
const subject = parts[1];
|
|
173
|
+
const body = parts.slice(2).join("|").trim();
|
|
174
|
+
|
|
175
|
+
// Skip CI commits
|
|
176
|
+
if (config.skipCI.some((skip) => subject.includes(skip))) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Parse conventional commit format: type(scope): subject
|
|
181
|
+
const conventionalMatch = subject.match(/^(\w+)(?:\(([^)]+)\))?!?:\s*(.+)$/);
|
|
182
|
+
|
|
183
|
+
if (!conventionalMatch) {
|
|
184
|
+
// Non-conventional commit, treat as misc
|
|
185
|
+
return {
|
|
186
|
+
hash,
|
|
187
|
+
type: "other",
|
|
188
|
+
scope: null,
|
|
189
|
+
subject,
|
|
190
|
+
body,
|
|
191
|
+
breaking: false,
|
|
192
|
+
raw: rawCommit,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const [, type, scope, message] = conventionalMatch;
|
|
197
|
+
const breaking = subject.includes("!:") ||
|
|
198
|
+
body.toLowerCase().includes("breaking change") ||
|
|
199
|
+
body.toLowerCase().includes("breaking-change");
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
hash,
|
|
203
|
+
type: type.toLowerCase(),
|
|
204
|
+
scope: scope || null,
|
|
205
|
+
subject: message.trim(),
|
|
206
|
+
body,
|
|
207
|
+
breaking,
|
|
208
|
+
raw: rawCommit,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Version Functions
|
|
214
|
+
// =============================================================================
|
|
215
|
+
|
|
216
|
+
function bumpVersion(version: string, bumpType: BumpType): string {
|
|
217
|
+
const parts = version.replace(/^v/, "").split(".");
|
|
218
|
+
const major = parseInt(parts[0] || "0", 10);
|
|
219
|
+
const minor = parseInt(parts[1] || "0", 10);
|
|
220
|
+
const patch = parseInt(parts[2] || "0", 10);
|
|
221
|
+
|
|
222
|
+
switch (bumpType) {
|
|
223
|
+
case "major":
|
|
224
|
+
return `${major + 1}.0.0`;
|
|
225
|
+
case "minor":
|
|
226
|
+
return `${major}.${minor + 1}.0`;
|
|
227
|
+
case "patch":
|
|
228
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function determineBumpType(commits: Commit[], config: ReleaseConfig): BumpType | null {
|
|
233
|
+
let bump: BumpType | null = null;
|
|
234
|
+
const priority: Record<BumpType, number> = { patch: 1, minor: 2, major: 3 };
|
|
235
|
+
|
|
236
|
+
for (const commit of commits) {
|
|
237
|
+
// Breaking changes always result in major bump
|
|
238
|
+
if (commit.breaking) {
|
|
239
|
+
return "major";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const typeConfig = config.types[commit.type];
|
|
243
|
+
if (typeConfig?.bump) {
|
|
244
|
+
if (!bump || priority[typeConfig.bump] > priority[bump]) {
|
|
245
|
+
bump = typeConfig.bump;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return bump;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// =============================================================================
|
|
254
|
+
// Changelog Generation
|
|
255
|
+
// =============================================================================
|
|
256
|
+
|
|
257
|
+
function generateChangelog(
|
|
258
|
+
commits: Commit[],
|
|
259
|
+
newVersion: string,
|
|
260
|
+
config: ReleaseConfig
|
|
261
|
+
): string {
|
|
262
|
+
const date = new Date().toISOString().split("T")[0];
|
|
263
|
+
const groupedCommits: Record<string, Commit[]> = {};
|
|
264
|
+
|
|
265
|
+
// Group commits by type
|
|
266
|
+
for (const commit of commits) {
|
|
267
|
+
const type = commit.type;
|
|
268
|
+
if (!groupedCommits[type]) {
|
|
269
|
+
groupedCommits[type] = [];
|
|
270
|
+
}
|
|
271
|
+
groupedCommits[type].push(commit);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let changelog = `## [${newVersion}] - ${date}\n\n`;
|
|
275
|
+
|
|
276
|
+
// Breaking changes section
|
|
277
|
+
const breakingChanges = commits.filter((c) => c.breaking);
|
|
278
|
+
if (breakingChanges.length > 0) {
|
|
279
|
+
changelog += `### ā ļø BREAKING CHANGES\n\n`;
|
|
280
|
+
for (const commit of breakingChanges) {
|
|
281
|
+
const scope = commit.scope ? `**${commit.scope}**: ` : "";
|
|
282
|
+
changelog += `- ${scope}${commit.subject} (${commit.hash.slice(0, 7)})\n`;
|
|
283
|
+
}
|
|
284
|
+
changelog += "\n";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Regular changes by type
|
|
288
|
+
for (const [type, typeConfig] of Object.entries(config.types)) {
|
|
289
|
+
if (typeConfig.hidden) continue;
|
|
290
|
+
|
|
291
|
+
const typeCommits = groupedCommits[type];
|
|
292
|
+
if (!typeCommits || typeCommits.length === 0) continue;
|
|
293
|
+
|
|
294
|
+
changelog += `### ${typeConfig.title}\n\n`;
|
|
295
|
+
for (const commit of typeCommits) {
|
|
296
|
+
const scope = commit.scope ? `**${commit.scope}**: ` : "";
|
|
297
|
+
changelog += `- ${scope}${commit.subject} (${commit.hash.slice(0, 7)})\n`;
|
|
298
|
+
}
|
|
299
|
+
changelog += "\n";
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Other/uncategorized commits
|
|
303
|
+
if (groupedCommits.other && groupedCommits.other.length > 0) {
|
|
304
|
+
changelog += `### š Other Changes\n\n`;
|
|
305
|
+
for (const commit of groupedCommits.other) {
|
|
306
|
+
changelog += `- ${commit.subject} (${commit.hash.slice(0, 7)})\n`;
|
|
307
|
+
}
|
|
308
|
+
changelog += "\n";
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return changelog;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function generateReleaseNotes(
|
|
315
|
+
commits: Commit[],
|
|
316
|
+
newVersion: string,
|
|
317
|
+
packageName: string,
|
|
318
|
+
config: ReleaseConfig
|
|
319
|
+
): string {
|
|
320
|
+
const changelog = generateChangelog(commits, newVersion, config);
|
|
321
|
+
|
|
322
|
+
return `# ${packageName} v${newVersion}
|
|
323
|
+
|
|
324
|
+
${changelog}
|
|
325
|
+
|
|
326
|
+
## Installation
|
|
327
|
+
|
|
328
|
+
\`\`\`bash
|
|
329
|
+
bun add ${packageName}@${newVersion}
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
or with npm:
|
|
333
|
+
|
|
334
|
+
\`\`\`bash
|
|
335
|
+
npm install ${packageName}@${newVersion}
|
|
336
|
+
\`\`\`
|
|
337
|
+
`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function updateChangelogFile(
|
|
341
|
+
newChangelog: string,
|
|
342
|
+
dryRun: boolean
|
|
343
|
+
): Promise<void> {
|
|
344
|
+
const changelogPath = join(process.cwd(), "CHANGELOG.md");
|
|
345
|
+
let existingChangelog = "";
|
|
346
|
+
|
|
347
|
+
if (existsSync(changelogPath)) {
|
|
348
|
+
existingChangelog = await Bun.file(changelogPath).text();
|
|
349
|
+
// Remove the header if it exists
|
|
350
|
+
existingChangelog = existingChangelog.replace(
|
|
351
|
+
/^# Changelog\n\n(?:.*\n)*?(?=## \[)/,
|
|
352
|
+
""
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const fullChangelog = `# Changelog
|
|
357
|
+
|
|
358
|
+
All notable changes to this project will be documented in this file.
|
|
359
|
+
|
|
360
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
361
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
362
|
+
|
|
363
|
+
${newChangelog}${existingChangelog}`;
|
|
364
|
+
|
|
365
|
+
if (dryRun) {
|
|
366
|
+
log("Would write CHANGELOG.md:", "info");
|
|
367
|
+
console.log(fullChangelog.slice(0, 500) + "...");
|
|
368
|
+
} else {
|
|
369
|
+
await Bun.write(changelogPath, fullChangelog);
|
|
370
|
+
log("Updated CHANGELOG.md", "success");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// =============================================================================
|
|
375
|
+
// Package.json Functions
|
|
376
|
+
// =============================================================================
|
|
377
|
+
|
|
378
|
+
async function readPackageJson(): Promise<PackageJson> {
|
|
379
|
+
const packagePath = join(process.cwd(), "package.json");
|
|
380
|
+
const content = await Bun.file(packagePath).text();
|
|
381
|
+
return JSON.parse(content);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function updatePackageJson(
|
|
385
|
+
newVersion: string,
|
|
386
|
+
dryRun: boolean
|
|
387
|
+
): Promise<void> {
|
|
388
|
+
const packagePath = join(process.cwd(), "package.json");
|
|
389
|
+
const packageJson = await readPackageJson();
|
|
390
|
+
packageJson.version = newVersion;
|
|
391
|
+
|
|
392
|
+
if (dryRun) {
|
|
393
|
+
log(`Would update package.json version to ${newVersion}`, "info");
|
|
394
|
+
} else {
|
|
395
|
+
await Bun.write(packagePath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
396
|
+
log(`Updated package.json version to ${newVersion}`, "success");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// =============================================================================
|
|
401
|
+
// GitHub Actions Output
|
|
402
|
+
// =============================================================================
|
|
403
|
+
|
|
404
|
+
async function setGitHubOutput(name: string, value: string): Promise<void> {
|
|
405
|
+
const outputFile = process.env.GITHUB_OUTPUT;
|
|
406
|
+
if (outputFile) {
|
|
407
|
+
const output = `${name}=${value}\n`;
|
|
408
|
+
// Use Node.js appendFileSync for reliable GitHub Actions output
|
|
409
|
+
const { appendFileSync } = await import("fs");
|
|
410
|
+
appendFileSync(outputFile, output);
|
|
411
|
+
log(`Set output: ${name}=${value}`, "info");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// =============================================================================
|
|
416
|
+
// Load Custom Config
|
|
417
|
+
// =============================================================================
|
|
418
|
+
|
|
419
|
+
async function loadConfig(): Promise<ReleaseConfig> {
|
|
420
|
+
const configPaths = [
|
|
421
|
+
join(process.cwd(), ".releaserc.json"),
|
|
422
|
+
join(process.cwd(), "release.config.json"),
|
|
423
|
+
join(process.cwd(), ".release.json"),
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
for (const configPath of configPaths) {
|
|
427
|
+
if (existsSync(configPath)) {
|
|
428
|
+
const content = await Bun.file(configPath).text();
|
|
429
|
+
const customConfig = JSON.parse(content);
|
|
430
|
+
return { ...DEFAULT_CONFIG, ...customConfig };
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return DEFAULT_CONFIG;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// =============================================================================
|
|
438
|
+
// Main Release Function
|
|
439
|
+
// =============================================================================
|
|
440
|
+
|
|
441
|
+
async function main(): Promise<void> {
|
|
442
|
+
console.log("\nš Zenith Release Script\n");
|
|
443
|
+
console.log("=".repeat(50) + "\n");
|
|
444
|
+
|
|
445
|
+
const { dryRun, bumpType: forcedBumpType } = parseArgs();
|
|
446
|
+
|
|
447
|
+
if (dryRun) {
|
|
448
|
+
log("Running in DRY RUN mode - no changes will be made", "warn");
|
|
449
|
+
console.log();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Load configuration
|
|
453
|
+
const config = await loadConfig();
|
|
454
|
+
log("Loaded release configuration", "success");
|
|
455
|
+
|
|
456
|
+
// Read package.json
|
|
457
|
+
const packageJson = await readPackageJson();
|
|
458
|
+
log(`Package: ${packageJson.name}`, "info");
|
|
459
|
+
log(`Current version: ${packageJson.version}`, "info");
|
|
460
|
+
|
|
461
|
+
// Get last tag
|
|
462
|
+
const lastTag = await getLastTag();
|
|
463
|
+
log(`Last tag: ${lastTag || "none"}`, "info");
|
|
464
|
+
|
|
465
|
+
// Get commits since last tag
|
|
466
|
+
const rawCommits = await getCommitsSinceTag(lastTag);
|
|
467
|
+
log(`Found ${rawCommits.length} commits since last release`, "info");
|
|
468
|
+
|
|
469
|
+
if (rawCommits.length === 0) {
|
|
470
|
+
log("No commits since last release. Nothing to do.", "warn");
|
|
471
|
+
await setGitHubOutput("should_release", "false");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Parse commits
|
|
476
|
+
const commits = rawCommits
|
|
477
|
+
.map((c) => parseCommit(c, config))
|
|
478
|
+
.filter((c): c is Commit => c !== null);
|
|
479
|
+
|
|
480
|
+
log(`Parsed ${commits.length} conventional commits`, "info");
|
|
481
|
+
|
|
482
|
+
// Determine bump type
|
|
483
|
+
const determinedBumpType = forcedBumpType || determineBumpType(commits, config);
|
|
484
|
+
|
|
485
|
+
if (!determinedBumpType) {
|
|
486
|
+
log("No version bump required based on commits", "warn");
|
|
487
|
+
await setGitHubOutput("should_release", "false");
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
log(`Version bump type: ${determinedBumpType}`, "info");
|
|
492
|
+
|
|
493
|
+
// Calculate new version
|
|
494
|
+
const newVersion = bumpVersion(packageJson.version, determinedBumpType);
|
|
495
|
+
log(`New version: ${newVersion}`, "success");
|
|
496
|
+
|
|
497
|
+
// Check if this version already exists as a tag (fallback to prevent duplicates)
|
|
498
|
+
if (await tagExists(newVersion)) {
|
|
499
|
+
log(`Version v${newVersion} already exists as a tag. Skipping release.`, "warn");
|
|
500
|
+
await setGitHubOutput("should_release", "false");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
console.log("\n" + "-".repeat(50) + "\n");
|
|
505
|
+
|
|
506
|
+
// Generate changelog
|
|
507
|
+
const changelog = generateChangelog(commits, newVersion, config);
|
|
508
|
+
log("Generated changelog", "success");
|
|
509
|
+
|
|
510
|
+
// Generate release notes
|
|
511
|
+
const releaseNotes = generateReleaseNotes(
|
|
512
|
+
commits,
|
|
513
|
+
newVersion,
|
|
514
|
+
packageJson.name,
|
|
515
|
+
config
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// Update files
|
|
519
|
+
await updateChangelogFile(changelog, dryRun);
|
|
520
|
+
await updatePackageJson(newVersion, dryRun);
|
|
521
|
+
|
|
522
|
+
// Write release notes for GitHub Action
|
|
523
|
+
if (!dryRun) {
|
|
524
|
+
await Bun.write(join(process.cwd(), "RELEASE_NOTES.md"), releaseNotes);
|
|
525
|
+
log("Written RELEASE_NOTES.md", "success");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Set GitHub Actions outputs
|
|
529
|
+
await setGitHubOutput("should_release", "true");
|
|
530
|
+
await setGitHubOutput("new_version", newVersion);
|
|
531
|
+
await setGitHubOutput("bump_type", determinedBumpType);
|
|
532
|
+
await setGitHubOutput("package_name", packageJson.name);
|
|
533
|
+
|
|
534
|
+
console.log("\n" + "=".repeat(50));
|
|
535
|
+
console.log("\nā
Release preparation complete!\n");
|
|
536
|
+
|
|
537
|
+
if (dryRun) {
|
|
538
|
+
console.log("DRY RUN - Summary of what would happen:");
|
|
539
|
+
console.log(` ⢠Version: ${packageJson.version} ā ${newVersion}`);
|
|
540
|
+
console.log(` ⢠CHANGELOG.md would be updated`);
|
|
541
|
+
console.log(` ⢠Release notes would be created`);
|
|
542
|
+
console.log(` ⢠Git tag: v${newVersion} would be created`);
|
|
543
|
+
console.log(` ⢠GitHub release would be published`);
|
|
544
|
+
if (!packageJson.private) {
|
|
545
|
+
console.log(` ⢠Package would be published to NPM`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Run the script
|
|
551
|
+
main().catch((error) => {
|
|
552
|
+
log(`Release failed: ${error.message}`, "error");
|
|
553
|
+
process.exit(1);
|
|
554
|
+
});
|