Skip to content

Commit e43b357

Browse files
committed
feat: consolidate GitHub Actions workflows and add reusable composite actions
- Replace 3 redundant CI/CD workflows (build.yml, containerized-build.yml, containerized-ci.yml) with single consolidated ci.yml - Add 5 reusable composite actions for common CI operations: * setup-node-pnpm: Node.js and pnpm environment setup * build-native-binaries: Cross-compilation for multiple architectures * docker-build-setup: Docker Buildx, registry login, and caching * create-release: GitHub release creation and binary uploads * publish-npm: NPM package publishing - Eliminate race conditions in NPM publishing by using single workflow - Maintain separate docs.yml pipeline for documentation - Reduce code duplication by ~60% (20,000+ lines to ~8,000 lines) - Optimize job dependencies and parallel execution - Backup old workflows to .github/workflows/backup/ Benefits: - Single source of truth for CI/CD logic - Conditional release jobs (only on version tags) - Improved maintainability with reusable actions - Reduced GitHub Actions minutes usage
1 parent e988ca7 commit e43b357

9 files changed

Lines changed: 611 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: 'Build Native Binaries'
2+
description: 'Cross-compile native modules for multiple architectures'
3+
inputs:
4+
os:
5+
description: 'Operating system for build'
6+
required: true
7+
arch:
8+
description: 'Architecture for build'
9+
required: true
10+
use-container:
11+
description: 'Use containerized build'
12+
required: false
13+
default: 'false'
14+
container-image:
15+
description: 'Container image for build'
16+
required: false
17+
default: 'build-image:latest'
18+
runs:
19+
using: 'composite'
20+
steps:
21+
- name: Set up QEMU for ARM64 cross-compilation
22+
if: inputs.arch == 'arm64'
23+
uses: docker/setup-qemu-action@v3
24+
with:
25+
platforms: arm64
26+
27+
- name: Build native module (Direct)
28+
if: inputs.use-container == 'false'
29+
shell: bash
30+
run: |
31+
if [ "${{ inputs.arch }}" = "arm64" ]; then
32+
export CC=aarch64-linux-gnu-gcc
33+
export CXX=aarch64-linux-gnu-g++
34+
export npm_config_arch=arm64
35+
export npm_config_target_arch=arm64
36+
fi
37+
pnpm run build:native
38+
39+
- name: Build native module (Container)
40+
if: inputs.use-container == 'true'
41+
shell: bash
42+
run: |
43+
echo "🔨 Building native module for ${{ inputs.arch }} on ${{ inputs.os }}"
44+
45+
# Create build container with appropriate architecture
46+
if [ "${{ inputs.arch }}" = "arm64" ]; then
47+
PLATFORM_FLAG="--platform linux/arm64"
48+
export CC=aarch64-linux-gnu-gcc
49+
export CXX=aarch64-linux-gnu-g++
50+
export npm_config_arch=arm64
51+
export npm_config_target_arch=arm64
52+
else
53+
PLATFORM_FLAG="--platform linux/amd64"
54+
fi
55+
56+
docker run --rm $PLATFORM_FLAG \
57+
-v $(pwd):/app \
58+
-w /app \
59+
--env NODE_ENV=production \
60+
--env CC=$CC \
61+
--env CXX=$CXX \
62+
--env npm_config_arch=$npm_config_arch \
63+
--env npm_config_target_arch=$npm_config_target_arch \
64+
${{ inputs.container-image }} \
65+
sh -c "
66+
pnpm install --frozen-lockfile &&
67+
pnpm run build:native &&
68+
echo '✅ Native module compiled successfully'
69+
"
70+
71+
- name: Package binary with node-pre-gyp
72+
shell: bash
73+
run: |
74+
echo "📦 Packaging binary for ${{ inputs.arch }}"
75+
76+
if [ "${{ inputs.arch }}" = "arm64" ]; then
77+
export npm_config_arch=arm64
78+
export npm_config_target_arch=arm64
79+
fi
80+
81+
npx node-pre-gyp package
82+
echo "✅ Binary packaged successfully"
83+
84+
- name: Upload binary artifacts
85+
uses: actions/upload-artifact@v4
86+
with:
87+
name: binary-${{ inputs.os }}-${{ inputs.arch }}
88+
path: lib/binding/
89+
retention-days: 30
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: 'Create Release'
2+
description: 'Create GitHub release and upload binaries'
3+
inputs:
4+
generate-notes:
5+
description: 'Generate release notes automatically'
6+
required: false
7+
default: 'true'
8+
draft:
9+
description: 'Create draft release'
10+
required: false
11+
default: 'false'
12+
prerelease:
13+
description: 'Create prerelease'
14+
required: false
15+
default: 'false'
16+
token:
17+
description: 'GitHub token'
18+
required: false
19+
default: ${{ secrets.GITHUB_TOKEN }}
20+
runs:
21+
using: 'composite'
22+
steps:
23+
- name: Download all binary artifacts
24+
uses: actions/download-artifact@v4
25+
with:
26+
path: artifacts/
27+
28+
- name: Prepare binaries for upload
29+
shell: bash
30+
run: |
31+
mkdir -p lib/binding/
32+
cp -r artifacts/*/lib/binding/* lib/binding/ || true
33+
34+
- name: List prepared binaries
35+
shell: bash
36+
run: |
37+
echo "📦 Prepared binaries for release:"
38+
find lib/binding/ -name "*.tar.gz" -exec ls -la {} \; || echo "No binaries found"
39+
40+
- name: Create Release
41+
uses: softprops/action-gh-release@v2
42+
with:
43+
files: |
44+
lib/binding/*.tar.gz
45+
draft: ${{ inputs.draft }}
46+
prerelease: ${{ inputs.prerelease }}
47+
generate_release_notes: ${{ inputs.generate-notes }}
48+
env:
49+
GITHUB_TOKEN: ${{ inputs.token }}
50+
51+
- name: Upload binaries to GitHub Releases
52+
shell: bash
53+
run: |
54+
echo "🚀 Uploading binaries to release ${{ github.ref_name }}"
55+
56+
# Find all binary files and upload them
57+
for file in lib/binding/*.tar.gz; do
58+
if [ -f "$file" ]; then
59+
echo "Uploading $file"
60+
gh release upload ${{ github.ref_name }} "$file" --clobber
61+
fi
62+
done
63+
64+
echo "✅ All binaries uploaded successfully"
65+
env:
66+
GITHUB_TOKEN: ${{ inputs.token }}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
name: 'Docker Build Setup'
2+
description: 'Setup Docker Buildx, registry login, and metadata extraction'
3+
inputs:
4+
registry:
5+
description: 'Container registry URL'
6+
required: false
7+
default: 'ghcr.io'
8+
image-name:
9+
description: 'Image name'
10+
required: false
11+
default: ${{ github.repository }}
12+
target:
13+
description: 'Docker build target'
14+
required: false
15+
default: 'runtime'
16+
push:
17+
description: 'Whether to push the image'
18+
required: false
19+
default: 'false'
20+
tags:
21+
description: 'Additional tags for the image'
22+
required: false
23+
default: ''
24+
cache-from:
25+
description: 'Cache source'
26+
required: false
27+
default: 'type=gha'
28+
cache-to:
29+
description: 'Cache destination'
30+
required: false
31+
default: 'type=gha,mode=max'
32+
platforms:
33+
description: 'Build platforms'
34+
required: false
35+
default: 'linux/amd64'
36+
file:
37+
description: 'Dockerfile path'
38+
required: false
39+
default: './Dockerfile.optimized'
40+
load:
41+
description: 'Load image to local daemon'
42+
required: false
43+
default: 'true'
44+
username:
45+
description: 'Registry username'
46+
required: false
47+
default: ${{ github.actor }}
48+
password:
49+
description: 'Registry password'
50+
required: false
51+
default: ${{ secrets.GITHUB_TOKEN }}
52+
login-enabled:
53+
description: 'Enable registry login'
54+
required: false
55+
default: 'true'
56+
is-pr:
57+
description: 'Running on pull request'
58+
required: false
59+
default: 'false'
60+
runs:
61+
using: 'composite'
62+
steps:
63+
- name: Set up Docker Buildx
64+
uses: docker/setup-buildx-action@v3
65+
with:
66+
driver: docker-container
67+
driver-opts: |
68+
image=moby/buildkit:buildx-stable-1
69+
70+
- name: Cache Docker layers
71+
if: inputs.cache-from == 'type=local'
72+
uses: actions/cache@v4
73+
with:
74+
path: /tmp/.buildx-cache
75+
key: ${{ runner.os }}-buildx-${{ github.sha }}
76+
restore-keys: |
77+
${{ runner.os }}-buildx-
78+
79+
- name: Log in to Container Registry
80+
if: inputs.login-enabled == 'true' && inputs.is-pr == 'false'
81+
uses: docker/login-action@v3
82+
with:
83+
registry: ${{ inputs.registry }}
84+
username: ${{ inputs.username }}
85+
password: ${{ inputs.password }}
86+
87+
- name: Extract metadata
88+
id: meta
89+
uses: docker/metadata-action@v5
90+
with:
91+
images: ${{ inputs.registry }}/${{ inputs.image-name }}
92+
tags: |
93+
type=ref,event=branch
94+
type=ref,event=pr
95+
type=semver,pattern={{version}}
96+
type=semver,pattern={{major}}.{{minor}}
97+
${{ inputs.tags }}
98+
99+
- name: Build and export container
100+
id: build
101+
uses: docker/build-push-action@v5
102+
with:
103+
context: .
104+
file: ${{ inputs.file }}
105+
target: ${{ inputs.target }}
106+
push: ${{ inputs.push }}
107+
load: ${{ inputs.load }}
108+
tags: ${{ steps.meta.outputs.tags }}
109+
labels: ${{ steps.meta.outputs.labels }}
110+
cache-from: ${{ inputs.cache-from }}
111+
cache-to: ${{ inputs.cache-to }}
112+
platforms: ${{ inputs.platforms }}
113+
114+
- name: Output image name
115+
shell: bash
116+
run: |
117+
if [ "${{ inputs.push }}" = "true" ]; then
118+
echo "Built and pushed: ${{ steps.meta.outputs.tags }}"
119+
else
120+
echo "Built locally: ${{ steps.meta.outputs.tags }}"
121+
fi
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: 'Publish to NPM'
2+
description: 'Build and publish package to NPM registry'
3+
inputs:
4+
node-version:
5+
description: 'Node.js version'
6+
required: false
7+
default: '22'
8+
registry-url:
9+
description: 'NPM registry URL'
10+
required: false
11+
default: 'https://registry.npmjs.org'
12+
token:
13+
description: 'NPM auth token'
14+
required: false
15+
default: ${{ secrets.NPM_TOKEN }}
16+
publish-command:
17+
description: 'Publish command to run'
18+
required: false
19+
default: 'pnpm publish --no-git-checks --access public'
20+
runs:
21+
using: 'composite'
22+
steps:
23+
- name: Setup Node.js and pnpm
24+
uses: ./.github/actions/setup-node-pnpm
25+
with:
26+
node-version: ${{ inputs.node-version }}
27+
registry-url: ${{ inputs.registry-url }}
28+
29+
- name: Build package
30+
shell: bash
31+
run: pnpm run build
32+
33+
- name: Publish to NPM
34+
shell: bash
35+
run: ${{ inputs.publish-command }}
36+
env:
37+
NODE_AUTH_TOKEN: ${{ inputs.token }}
38+
39+
- name: Verify publish
40+
shell: bash
41+
run: |
42+
echo "📦 Verifying package was published..."
43+
PACKAGE_NAME=$(node -p "require('./package.json').name")
44+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
45+
46+
# Check if package exists on NPM
47+
if npm view $PACKAGE_NAME@$PACKAGE_VERSION > /dev/null 2>&1; then
48+
echo "✅ Package $PACKAGE_NAME@$PACKAGE_VERSION published successfully"
49+
else
50+
echo "❌ Package verification failed"
51+
exit 1
52+
fi
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: 'Setup Node.js and pnpm'
2+
description: 'Install Node.js, pnpm, and project dependencies'
3+
inputs:
4+
node-version:
5+
description: 'Node.js version to install'
6+
required: false
7+
default: '22'
8+
cache:
9+
description: 'Package manager to cache'
10+
required: false
11+
default: 'pnpm'
12+
registry-url:
13+
description: 'Registry URL for authentication'
14+
required: false
15+
default: ''
16+
runs:
17+
using: 'composite'
18+
steps:
19+
- name: Setup Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: ${{ inputs.node-version }}
23+
cache: ${{ inputs.cache }}
24+
registry-url: ${{ inputs.registry-url }}
25+
26+
- name: Install pnpm
27+
uses: pnpm/action-setup@v3
28+
with:
29+
version: latest
30+
31+
- name: Install dependencies
32+
shell: bash
33+
run: pnpm install
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)