name: CI/CD
on:
push:
branches: [main, release, rc]
pull_request:
branches: [main, release, rc]
workflow_dispatch:
inputs:
target_branch:
description: 'Target branch for release'
required: true
default: 'release'
type: choice
options:
- release
- rc
release_type:
description: 'Release type'
required: true
default: 'stable'
type: choice
options:
- stable
- rc
skip_lint:
description: 'Skip lint check (emergency releases only)'
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
checks: write
pull-requests: write
id-token: write
attestations: write
jobs:
ci-cd:
name: CI/CD Pipeline
runs-on: ubuntu-latest
environment: ${{ ((github.event_name == 'push' && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/rc')) || (github.event_name == 'workflow_dispatch')) && 'production' || null }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Smart fetch: full history for releases (changelog), shallow for others
fetch-depth: ${{ ((github.event_name == 'push' && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/rc')) || (github.event_name == 'workflow_dispatch' && inputs.release_type != 'skip')) && 0 || 1 }}
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || github.ref }}
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.15
- name: Cache Dependencies
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('**/package.json', '**/bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install Dependencies
run: bun install
- name: Lint & Build (Parallel)
run: |
if [ "${{ github.event_name == 'workflow_dispatch' && inputs.skip_lint == true }}" = "true" ]; then
echo "⚠️ Skipping lint check (emergency release mode)"
echo "🔨 Running build only..."
bun run build
else
echo "🚀 Running lint and build in parallel..."
bun run format:check &
LINT_PID=$!
bun run build &
BUILD_PID=$!
# Wait for both processes and capture exit codes
wait $LINT_PID
LINT_EXIT=$?
wait $BUILD_PID
BUILD_EXIT=$?
# Check if either failed
if [ $LINT_EXIT -ne 0 ]; then
echo "❌ Lint failed"
exit $LINT_EXIT
fi
if [ $BUILD_EXIT -ne 0 ]; then
echo "❌ Build failed"
exit $BUILD_EXIT
fi
echo "✅ Lint and build completed successfully"
fi
- name: Attest Build Artifacts
if: |
(github.event_name == 'push' && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/rc')) ||
(github.event_name == 'workflow_dispatch' && inputs.release_type != 'skip')
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/*'
- name: Release
if: |
(github.event_name == 'push' && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/rc')) ||
(github.event_name == 'workflow_dispatch' && inputs.release_type != 'skip')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
run: |
echo "🔧 Configuring git and npm..."
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" >> ~/.npmrc
# Determine release type (automatic vs manual)
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "📋 Manual release triggered:"
echo " - Target branch: ${{ inputs.target_branch }}"
echo " - Release type: ${{ inputs.release_type }}"
if [ "${{ inputs.release_type }}" = "stable" ]; then
echo "🚀 Releasing stable version..."
bun run release:ci
elif [ "${{ inputs.release_type }}" = "rc" ]; then
echo "🚀 Releasing RC version..."
bun run release:rc:ci
fi
else
echo "🤖 Automatic release triggered by push"
if [ "${{ github.ref }}" = "refs/heads/release" ]; then
echo "🚀 Releasing stable version..."
bun run release:ci
elif [ "${{ github.ref }}" = "refs/heads/rc" ]; then
echo "🚀 Releasing RC version..."
bun run release:rc:ci
fi
fi