@@ -3,14 +3,26 @@ name: Rust Release
33on :
44 push :
55 tags :
6- - ' v* '
6+ - " v* "
77 workflow_dispatch :
8+ inputs :
9+ tag :
10+ description : " Tag to release (e.g. v2.0.0). Leave empty when triggered from tag push."
11+ required : false
12+ type : string
13+
14+ permissions :
15+ contents : read
16+
17+ env :
18+ CARGO_TERM_COLOR : always
819
920jobs :
1021 build :
1122 name : Build ${{ matrix.platform }}
1223 runs-on : ${{ matrix.os }}
1324 strategy :
25+ fail-fast : false
1426 matrix :
1527 include :
1628 - os : ubuntu-latest
3850 - name : Checkout
3951 uses : actions/checkout@v4
4052
53+ - name : Resolve release version
54+ id : version
55+ shell : bash
56+ run : |
57+ set -euo pipefail
58+
59+ TAG_INPUT="${{ inputs.tag }}"
60+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
61+ VERSION="${GITHUB_REF_NAME#v}"
62+ elif [[ -n "${TAG_INPUT}" ]]; then
63+ VERSION="${TAG_INPUT#v}"
64+ else
65+ echo "::error::workflow_dispatch requires inputs.tag when not running on a tag ref."
66+ exit 1
67+ fi
68+
69+ echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
70+ echo "Release version: ${VERSION}"
71+
4172 - name : Setup Rust
4273 uses : dtolnay/rust-toolchain@stable
4374 with :
@@ -47,18 +78,84 @@ jobs:
4778 if : matrix.platform == 'linux-arm64'
4879 run : |
4980 sudo apt-get update
50- sudo apt-get install -y gcc-aarch64-linux-gnu
81+ sudo apt-get install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross
82+
83+ - name : Configure linker (Linux ARM64)
84+ if : matrix.platform == 'linux-arm64'
85+ shell : bash
86+ run : |
87+ mkdir -p ~/.cargo
88+ cat >> ~/.cargo/config.toml <<EOF
89+ [target.aarch64-unknown-linux-gnu]
90+ linker = "aarch64-linux-gnu-gcc"
91+ EOF
5192
5293 - name : Build
5394 run : cargo build --release --target ${{ matrix.target }}
5495
55- - name : Copy binary to platform package
96+ - name : Prepare platform package
5697 shell : bash
5798 run : |
58- mkdir -p npm-platform/${{ matrix.platform }}/bin
59- cp target/${{ matrix.target }}/release/${{ matrix.binary }} npm-platform/${{ matrix.platform }}/bin/
99+ set -euo pipefail
100+
101+ case "${{ matrix.platform }}" in
102+ linux-x64)
103+ NPM_OS="linux"
104+ NPM_CPU="x64"
105+ ;;
106+ linux-arm64)
107+ NPM_OS="linux"
108+ NPM_CPU="arm64"
109+ ;;
110+ darwin-x64)
111+ NPM_OS="darwin"
112+ NPM_CPU="x64"
113+ ;;
114+ darwin-arm64)
115+ NPM_OS="darwin"
116+ NPM_CPU="arm64"
117+ ;;
118+ win32-x64)
119+ NPM_OS="win32"
120+ NPM_CPU="x64"
121+ ;;
122+ *)
123+ echo "Unsupported platform: ${{ matrix.platform }}"
124+ exit 1
125+ ;;
126+ esac
127+
128+ PACKAGE_DIR="npm-platform/${{ matrix.platform }}"
129+ mkdir -p "${PACKAGE_DIR}/bin"
130+ cp "target/${{ matrix.target }}/release/${{ matrix.binary }}" "${PACKAGE_DIR}/bin/"
131+
132+ cat > "${PACKAGE_DIR}/package.json" <<JSON
133+ {
134+ "name": "@dongowu/git-ai-cli-${{ matrix.platform }}",
135+ "version": "${{ steps.version.outputs.version }}",
136+ "description": "Platform binary for @dongowu/git-ai-cli (${{ matrix.platform }})",
137+ "license": "Apache-2.0",
138+ "repository": {
139+ "type": "git",
140+ "url": "https://github.com/dongowu/git-ai-cli"
141+ },
142+ "os": ["${NPM_OS}"],
143+ "cpu": ["${NPM_CPU}"],
144+ "files": ["bin/${{ matrix.binary }}"],
145+ "publishConfig": {
146+ "access": "public"
147+ }
148+ }
149+ JSON
150+
151+ cat > "${PACKAGE_DIR}/README.md" <<EOF
152+ # @dongowu/git-ai-cli-${{ matrix.platform }}
60153
61- - name : Upload artifact
154+ Platform-specific binary package for \
155+ [@dongowu/git-ai-cli](https://www.npmjs.com/package/@dongowu/git-ai-cli).
156+ EOF
157+
158+ - name : Upload platform package artifact
62159 uses : actions/upload-artifact@v4
63160 with :
64161 name : ${{ matrix.platform }}
@@ -68,52 +165,113 @@ jobs:
68165 name : Publish to npm
69166 needs : build
70167 runs-on : ubuntu-latest
71- if : startsWith(github.ref, 'refs/tags/v')
168+ permissions :
169+ contents : write
170+ id-token : write
72171
73172 steps :
74173 - name : Checkout
75174 uses : actions/checkout@v4
76175
176+ - name : Resolve release version
177+ id : version
178+ shell : bash
179+ run : |
180+ set -euo pipefail
181+
182+ TAG_INPUT="${{ inputs.tag }}"
183+ if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
184+ VERSION="${GITHUB_REF_NAME#v}"
185+ elif [[ -n "${TAG_INPUT}" ]]; then
186+ VERSION="${TAG_INPUT#v}"
187+ else
188+ echo "::error::workflow_dispatch requires inputs.tag when not running on a tag ref."
189+ exit 1
190+ fi
191+
192+ echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
193+ echo "Release version: ${VERSION}"
194+
77195 - name : Setup Node.js
78196 uses : actions/setup-node@v4
79197 with :
80- node-version : ' 18 '
81- registry-url : ' https://registry.npmjs.org'
198+ node-version : " 20 "
199+ registry-url : " https://registry.npmjs.org"
82200
83- - name : Download all artifacts
201+ - name : Download all platform artifacts
84202 uses : actions/download-artifact@v4
85203 with :
86204 path : npm-platform
87205
206+ - name : Align root package version with tag
207+ env :
208+ RELEASE_VERSION : ${{ steps.version.outputs.version }}
209+ run : |
210+ node <<"NODE"
211+ const fs = require("fs");
212+
213+ const version = process.env.RELEASE_VERSION;
214+ const pkgPath = "package.json";
215+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
216+
217+ pkg.version = version;
218+ pkg.optionalDependencies = pkg.optionalDependencies || {};
219+
220+ for (const platform of ["linux-x64", "linux-arm64", "darwin-x64", "darwin-arm64", "win32-x64"]) {
221+ pkg.optionalDependencies[`@dongowu/git-ai-cli-${platform}`] = version;
222+ }
223+
224+ fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
225+ NODE
226+
88227 - name : Publish platform packages
89228 env :
90229 NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
230+ shell : bash
91231 run : |
92- for platform in linux-x64 linux-arm64 darwin-x64 darwin-arm64 win32-x64; do
93- cd npm-platform/$platform
94- npm publish --access public
95- cd ../..
232+ set -euo pipefail
233+
234+ for package_dir in npm-platform/*; do
235+ if [[ ! -d "${package_dir}" || ! -f "${package_dir}/package.json" ]]; then
236+ continue
237+ fi
238+
239+ package_name=$(node -e "console.log(require(process.argv[1]).name)" "${package_dir}/package.json")
240+ package_version=$(node -e "console.log(require(process.argv[1]).version)" "${package_dir}/package.json")
241+
242+ if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
243+ echo "Skipping ${package_name}@${package_version} (already published)."
244+ continue
245+ fi
246+
247+ echo "Publishing ${package_name}@${package_version}"
248+ npm publish "${package_dir}" --access public --provenance
96249 done
97250
98251 - name : Publish main package
99252 env :
100253 NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
254+ shell : bash
101255 run : |
102- cd npm
103- npm publish --access public
256+ set -euo pipefail
257+
258+ package_name=$(node -e "console.log(require(process.argv[1]).name)" "./package.json")
259+ package_version=$(node -e "console.log(require(process.argv[1]).version)" "./package.json")
260+
261+ if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then
262+ echo "Skipping ${package_name}@${package_version} (already published)."
263+ exit 0
264+ fi
265+
266+ echo "Publishing ${package_name}@${package_version}"
267+ npm publish --access public --provenance
104268
105269 - name : Create GitHub Release
106- uses : softprops/action-gh-release@v1
270+ uses : softprops/action-gh-release@v2
107271 with :
272+ tag_name : v${{ steps.version.outputs.version }}
273+ generate_release_notes : true
108274 files : |
109275 npm-platform/*/bin/*
110- body : |
111- ## Installation
112- ```bash
113- npm install -g @dongowu/git-ai-cli
114- ```
115-
116- ## What's Changed
117- See commit history for details.
118276 env :
119277 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
0 commit comments