name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
create-release:
name: Create Release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ steps.get_version.outputs.version }}
steps:
- name: Get version from tag
id: get_version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
build:
name: Build for ${{ matrix.os }} (${{ matrix.target }})
needs: create-release
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: hbsx.exe
asset_name: hbsx-windows-x86_64.exe
- os: windows-latest
target: aarch64-pc-windows-msvc
artifact_name: hbsx.exe
asset_name: hbsx-windows-aarch64.exe
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: hbsx
asset_name: hbsx-linux-x86_64
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
artifact_name: hbsx
asset_name: hbsx-linux-aarch64
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
artifact_name: hbsx
asset_name: hbsx-linux-x86_64-musl
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: hbsx
asset_name: hbsx-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: hbsx
asset_name: hbsx-macos-aarch64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Update Cargo.toml version
shell: python
run: |
import re
version = "${{ needs.create-release.outputs.version }}"
print(f"Updating Cargo.toml to version {version}")
with open('Cargo.toml', 'r', encoding='utf-8') as f:
content = f.read()
# 更新 version 字段(在 [package] 部分)
content = re.sub(
r'^version\s*=\s*"[^"]*"',
f'version = "{version}"',
content,
count=1,
flags=re.MULTILINE
)
with open('Cargo.toml', 'w', encoding='utf-8') as f:
f.write(content)
# 验证更新
with open('Cargo.toml', 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('version ='):
print(f"Updated: {line.strip()}")
break
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install musl tools (Linux musl)
if: matrix.target == 'x86_64-unknown-linux-musl'
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }}
- name: Cache target directory
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-${{ matrix.target }}-cargo-target-${{ hashFiles('**/Cargo.lock') }}
- name: Show build environment
run: |
echo "Target: ${{ matrix.target }}"
echo "OS: ${{ matrix.os }}"
echo "Architecture: $(uname -m)"
echo "Rust version:"
rustc --version
cargo --version
- name: Build
run: cargo build --release --target ${{ matrix.target }} --verbose
- name: Strip binary (Linux and macOS)
if: matrix.os != 'windows-latest'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Compress binary (Linux and macOS)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
tar czf ${{ matrix.asset_name }}.tar.gz ${{ matrix.artifact_name }}
mv ${{ matrix.asset_name }}.tar.gz ../../../
- name: Compress binary (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
cd target/${{ matrix.target }}/release
Compress-Archive -Path ${{ matrix.artifact_name }} -DestinationPath ../../../${{ matrix.asset_name }}.zip
- name: Upload Release Asset (Linux and macOS)
if: matrix.os != 'windows-latest'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./${{ matrix.asset_name }}.tar.gz
asset_name: ${{ matrix.asset_name }}.tar.gz
asset_content_type: application/gzip
- name: Upload Release Asset (Windows)
if: matrix.os == 'windows-latest'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./${{ matrix.asset_name }}.zip
asset_name: ${{ matrix.asset_name }}.zip
asset_content_type: application/zip
- name: Generate SHA256 checksums (Linux and macOS)
if: matrix.os != 'windows-latest'
run: |
sha256sum ${{ matrix.asset_name }}.tar.gz > ${{ matrix.asset_name }}.tar.gz.sha256
- name: Generate SHA256 checksums (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
$hash = (Get-FileHash -Path ${{ matrix.asset_name }}.zip -Algorithm SHA256).Hash.ToLower()
"$hash ${{ matrix.asset_name }}.zip" | Out-File -FilePath ${{ matrix.asset_name }}.zip.sha256 -Encoding utf8 -NoNewline
- name: Upload SHA256 checksum (Linux and macOS)
if: matrix.os != 'windows-latest'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./${{ matrix.asset_name }}.tar.gz.sha256
asset_name: ${{ matrix.asset_name }}.tar.gz.sha256
asset_content_type: text/plain
- name: Upload SHA256 checksum (Windows)
if: matrix.os == 'windows-latest'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./${{ matrix.asset_name }}.zip.sha256
asset_name: ${{ matrix.asset_name }}.zip.sha256
asset_content_type: text/plain
- name: Upload artifact for npm publishing
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
retention-days: 1
publish-npm:
name: Publish to npm
needs: [create-release, build]
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: List downloaded artifacts
run: |
echo "Downloaded artifacts:"
ls -R artifacts/
- name: Copy binaries to platform packages
run: |
# 创建 bin 目录
mkdir -p package/npm/platform-packages/win32-x64/bin
mkdir -p package/npm/platform-packages/win32-arm64/bin
mkdir -p package/npm/platform-packages/linux-x64/bin
mkdir -p package/npm/platform-packages/linux-arm64/bin
mkdir -p package/npm/platform-packages/darwin-x64/bin
mkdir -p package/npm/platform-packages/darwin-arm64/bin
# 复制 Windows 二进制
cp artifacts/binary-x86_64-pc-windows-msvc/hbsx.exe package/npm/platform-packages/win32-x64/bin/
cp artifacts/binary-aarch64-pc-windows-msvc/hbsx.exe package/npm/platform-packages/win32-arm64/bin/
# 复制 Linux 二进制 (使用 musl 版本作为主要版本)
cp artifacts/binary-x86_64-unknown-linux-musl/hbsx package/npm/platform-packages/linux-x64/bin/
cp artifacts/binary-aarch64-unknown-linux-gnu/hbsx package/npm/platform-packages/linux-arm64/bin/
# 复制 macOS 二进制
cp artifacts/binary-x86_64-apple-darwin/hbsx package/npm/platform-packages/darwin-x64/bin/
cp artifacts/binary-aarch64-apple-darwin/hbsx package/npm/platform-packages/darwin-arm64/bin/
# 设置执行权限
chmod +x package/npm/platform-packages/*/bin/hbsx
- name: Update package versions
run: |
VERSION="${{ needs.create-release.outputs.version }}"
echo "Updating packages to version $VERSION"
# 使用 Python 脚本更新所有 package.json 文件
cd package/npm
export VERSION
python3 << 'EOF'
import json
import os
import sys
version = os.environ.get('VERSION', '')
if not version:
print('ERROR: VERSION environment variable is not set')
sys.exit(1)
print(f'Updating to version {version}')
# 更新主包版本
with open('package.json', 'r', encoding='utf-8') as f:
pkg = json.load(f)
pkg['version'] = version
for dep in pkg.get('optionalDependencies', {}):
pkg['optionalDependencies'][dep] = version
with open('package.json', 'w', encoding='utf-8') as f:
json.dump(pkg, f, indent=2, ensure_ascii=False)
f.write('\n')
print(f'✓ Main package updated to {version}')
# 更新平台包版本
platforms = ['win32-x64', 'win32-arm64', 'linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64']
for platform in platforms:
pkg_path = f'platform-packages/{platform}/package.json'
with open(pkg_path, 'r', encoding='utf-8') as f:
pkg = json.load(f)
pkg['version'] = version
with open(pkg_path, 'w', encoding='utf-8') as f:
json.dump(pkg, f, indent=2, ensure_ascii=False)
f.write('\n')
print(f'✓ Platform package {platform} updated to {version}')
print(f'✓ All package versions updated to {version}')
EOF
# 验证版本更新
echo "Verifying version updates..."
MAIN_VERSION=$(node -p "require('./package.json').version")
if [ "$MAIN_VERSION" != "$VERSION" ]; then
echo "ERROR: Main package version mismatch: $MAIN_VERSION != $VERSION"
exit 1
fi
echo "✓ Version verification passed: $VERSION"
- name: Publish platform packages
run: |
cd package/npm
# 发布平台包
for platform in win32-x64 win32-arm64 linux-x64 linux-arm64 darwin-x64 darwin-arm64; do
echo "Publishing @jihuayu/hbsx-$platform..."
cd platform-packages/$platform
# 检查包是否已存在
if npm view @jihuayu/hbsx-$platform@${{ needs.create-release.outputs.version }} version 2>/dev/null; then
echo "⚠️ Package @jihuayu/hbsx-$platform@${{ needs.create-release.outputs.version }} already exists, skipping..."
else
npm publish --provenance --access public || { echo "❌ Failed to publish $platform"; exit 1; }
echo "✓ Published @jihuayu/hbsx-$platform"
fi
cd ../..
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish main package
run: |
cd package/npm
# 检查主包是否已存在
if npm view @jihuayu/hbsx@${{ needs.create-release.outputs.version }} version 2>/dev/null; then
echo "⚠️ Package @jihuayu/hbsx@${{ needs.create-release.outputs.version }} already exists, skipping..."
else
echo "Publishing @jihuayu/hbsx..."
npm publish --provenance --access public || { echo "❌ Failed to publish main package"; exit 1; }
echo "✓ Published @jihuayu/hbsx"
fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Summary
run: |
echo "✓ All npm packages published successfully!"
echo "Version: ${{ needs.create-release.outputs.version }}"
echo ""
echo "Install with:"
echo " npm install -g @jihuayu/hbsx@${{ needs.create-release.outputs.version }}"
publish-crates:
name: Publish to crates.io
needs: [create-release, build]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Update Cargo.toml version
shell: python
run: |
import re
version = "${{ needs.create-release.outputs.version }}"
print(f"Updating Cargo.toml to version {version}")
with open('Cargo.toml', 'r', encoding='utf-8') as f:
content = f.read()
# 更新 version 字段
content = re.sub(
r'^version\s*=\s*"[^"]*"',
f'version = "{version}"',
content,
count=1,
flags=re.MULTILINE
)
with open('Cargo.toml', 'w', encoding='utf-8') as f:
f.write(content)
print(f"✓ Cargo.toml updated to version {version}")
- name: Verify package
run: cargo package --allow-dirty
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing hbsx to crates.io..."
cargo publish --allow-dirty --token "$CARGO_REGISTRY_TOKEN"
echo "✓ Published to crates.io"
- name: Summary
run: |
echo "✓ Package published to crates.io successfully!"
echo "Version: ${{ needs.create-release.outputs.version }}"
echo ""
echo "Install with:"
echo " cargo install hbsx"