name: Publish to PyPI & NPM
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
jobs:
version-gate:
name: Version Gate
runs-on: ubuntu-latest
outputs:
pypi_should_publish: ${{ steps.check_pypi.outputs.should_publish }}
npm_should_publish: ${{ steps.check_npm.outputs.should_publish }}
version: ${{ steps.extract.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: 提取版本号
id: extract
run: |
# 从 tag 名提取版本:v0.6.0 → 0.6.0
VERSION="${GITHUB_REF_NAME#v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "📦 当前发布版本: $VERSION"
- name: 检查 PyPI 是否已发布
id: check_pypi
run: |
VERSION="${{ steps.extract.outputs.version }}"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/triviumdb/${VERSION}/json")
if [ "$HTTP_CODE" = "200" ]; then
echo "⏭️ PyPI 已存在 triviumdb==${VERSION},跳过发布"
echo "should_publish=false" >> "$GITHUB_OUTPUT"
else
echo "✅ PyPI 未找到 triviumdb==${VERSION},准备发布"
echo "should_publish=true" >> "$GITHUB_OUTPUT"
fi
- name: 检查 NPM 是否已发布
id: check_npm
run: |
VERSION="${{ steps.extract.outputs.version }}"
# npm view 返回版本号或空
EXISTING=$(npm view triviumdb@${VERSION} version 2>/dev/null || echo "")
if [ "$EXISTING" = "$VERSION" ]; then
echo "⏭️ NPM 已存在 triviumdb@${VERSION},跳过发布"
echo "should_publish=false" >> "$GITHUB_OUTPUT"
else
echo "✅ NPM 未找到 triviumdb@${VERSION},准备发布"
echo "should_publish=true" >> "$GITHUB_OUTPUT"
fi
build-wheels:
name: Build wheels (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
needs: version-gate
if: needs.version-gate.outputs.pypi_should_publish == 'true'
strategy:
fail-fast: false
matrix:
include:
- name: Linux x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
sdist: true
- name: Linux ARM64
runner: ubuntu-latest
target: aarch64-unknown-linux-gnu
sdist: false
- name: Windows x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
sdist: false
- name: macOS x86_64
runner: macos-latest target: x86_64-apple-darwin
sdist: false
- name: macOS ARM64
runner: macos-latest
target: aarch64-apple-darwin
sdist: false
steps:
- uses: actions/checkout@v4
- name: Setup Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@v1
env:
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
with:
command: build
target: ${{ matrix.target }}
args: --release --features python -o dist ${{ matrix.sdist && '--sdist' || '' }}
rust-toolchain: stable
manylinux: ${{ contains(matrix.target, 'aarch64') && '2_28' || 'auto' }}
docker-options: ${{ contains(matrix.target, 'aarch64-unknown-linux') && '-e JEMALLOC_SYS_WITH_LG_PAGE=16' || '' }}
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.name }}
path: dist
release:
name: Release to PyPI
runs-on: ubuntu-latest
needs: [version-gate, build-wheels]
if: needs.version-gate.outputs.pypi_should_publish == 'true'
steps:
- name: Download all wheels
uses: actions/download-artifact@v4
with:
path: dist
pattern: wheels-*
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
build-node:
name: Build Node addon (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
needs: version-gate
if: needs.version-gate.outputs.npm_should_publish == 'true'
strategy:
fail-fast: false
matrix:
include:
- name: Linux x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
- name: Linux ARM64
runner: ubuntu-latest
target: aarch64-unknown-linux-gnu
- name: Windows x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
- name: macOS x86_64
runner: macos-latest target: x86_64-apple-darwin
- name: macOS ARM64
runner: macos-latest
target: aarch64-apple-darwin
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compiler (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Install dependencies
run: npm install
- name: Build node module
run: npx napi build --platform --release --features nodejs --target ${{ matrix.target }}
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }}
- name: Upload node binding
uses: actions/upload-artifact@v4
with:
name: node-${{ matrix.name }}
path: "*.node"
release-npm:
name: Release to NPM
runs-on: ubuntu-latest
needs: [version-gate, build-node]
if: needs.version-gate.outputs.npm_should_publish == 'true'
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- name: Download all bindings
uses: actions/download-artifact@v4
with:
path: .
pattern: node-*
merge-multiple: true
- name: Generate JS Loader
run: |
cat << 'EOF' > index.js
const { platform, arch } = process;
let nativeBinding = null;
try {
if (platform === 'win32') { nativeBinding = require('./triviumdb.win32-x64-msvc.node'); }
else if (platform === 'darwin' && arch === 'arm64') { nativeBinding = require('./triviumdb.darwin-arm64.node'); }
else if (platform === 'darwin') { nativeBinding = require('./triviumdb.darwin-x64.node'); }
else if (platform === 'linux' && arch === 'arm64') { nativeBinding = require('./triviumdb.linux-arm64-gnu.node'); }
else { nativeBinding = require('./triviumdb.linux-x64-gnu.node'); }
} catch (e) {
console.error(`TriviumDB failed to load native binding for ${platform} ${arch}`, e);
throw e;
}
module.exports = nativeBinding;
EOF
- name: Publish to NPM
run: |
npm install
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
summary:
name: Release Summary
runs-on: ubuntu-latest
needs: [version-gate, release, release-npm]
if: always()
steps:
- name: 输出发布摘要
run: |
echo "## 📦 Release Summary: v${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.version-gate.outputs.pypi_should_publish }}" = "true" ]; then
echo "- **PyPI**: ✅ 已发布 triviumdb==${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
else
echo "- **PyPI**: ⏭️ 跳过(版本已存在)" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.version-gate.outputs.npm_should_publish }}" = "true" ]; then
echo "- **NPM**: ✅ 已发布 triviumdb@${{ needs.version-gate.outputs.version }}" >> $GITHUB_STEP_SUMMARY
else
echo "- **NPM**: ⏭️ 跳过(版本已存在)" >> $GITHUB_STEP_SUMMARY
fi