release.yml•8.51 kB
name: Release
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., v1.0.0)'
required: true
type: string
jobs:
validate-version:
name: Validate Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
else
VERSION="${{ github.ref_name }}"
fi
# Validate version format
if ! echo "$VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$'; then
echo "Error: Invalid version format. Expected: v1.0.0 or v1.0.0-beta.1"
exit 1
fi
# Check if prerelease
if echo "$VERSION" | grep -E '\-[a-zA-Z0-9]+'; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION (prerelease: $(echo "$VERSION" | grep -E '\-[a-zA-Z0-9]+' && echo "true" || echo "false"))"
run-tests:
name: Run Tests
needs: validate-version
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run lint
run: npm run lint
build-and-publish:
name: Build and Publish
needs: [validate-version, run-tests]
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Update package version
run: |
VERSION="${{ needs.validate-version.outputs.version }}"
VERSION_WITHOUT_V="${VERSION#v}"
npm version "$VERSION_WITHOUT_V" --no-git-tag-version
- name: Build package
run: |
# If build script exists, run it
if npm run | grep -q "build"; then
npm run build
fi
- name: Publish to npm
if: env.NPM_TOKEN != ''
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ "${{ needs.validate-version.outputs.is_prerelease }}" = "true" ]; then
npm publish --tag beta --access public
else
npm publish --access public
fi
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ needs.validate-version.outputs.version }}
ghcr.io/${{ github.repository }}:${{ needs.validate-version.outputs.is_prerelease == 'false' && 'latest' || 'beta' }}
cache-from: type=gha
cache-to: type=gha,mode=max
create-release:
name: Create GitHub Release
needs: [validate-version, build-and-publish]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
run: |
VERSION="${{ needs.validate-version.outputs.version }}"
# Get previous tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
echo "No previous tag found, including all commits"
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges)
else
echo "Generating changelog from $PREV_TAG to $VERSION"
COMMITS=$(git log "$PREV_TAG"..HEAD --pretty=format:"- %s (%h)" --no-merges)
fi
# Group commits by type
FEATURES=$(echo "$COMMITS" | grep -E "^- (feat|feature):" || true)
FIXES=$(echo "$COMMITS" | grep -E "^- (fix|bugfix):" || true)
DOCS=$(echo "$COMMITS" | grep -E "^- (docs|documentation):" || true)
CHORES=$(echo "$COMMITS" | grep -E "^- (chore|build|ci|test):" || true)
OTHERS=$(echo "$COMMITS" | grep -vE "^- (feat|feature|fix|bugfix|docs|documentation|chore|build|ci|test):" || true)
# Build changelog
{
echo "## What's Changed in $VERSION"
echo ""
if [ -n "$FEATURES" ]; then
echo "### 🚀 Features"
echo "$FEATURES"
echo ""
fi
if [ -n "$FIXES" ]; then
echo "### 🐛 Bug Fixes"
echo "$FIXES"
echo ""
fi
if [ -n "$DOCS" ]; then
echo "### 📚 Documentation"
echo "$DOCS"
echo ""
fi
if [ -n "$CHORES" ]; then
echo "### 🔧 Maintenance"
echo "$CHORES"
echo ""
fi
if [ -n "$OTHERS" ]; then
echo "### Other Changes"
echo "$OTHERS"
echo ""
fi
echo "## Docker"
echo ""
echo "Pull the latest image:"
echo '```bash'
echo "docker pull ghcr.io/${{ github.repository }}:$VERSION"
echo '```'
echo ""
if [ "${{ needs.validate-version.outputs.is_prerelease }}" = "false" ]; then
echo "Or use the \`latest\` tag:"
echo '```bash'
echo "docker pull ghcr.io/${{ github.repository }}:latest"
echo '```'
fi
echo ""
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG:-initial}...$VERSION"
} > changelog.md
# Save changelog for release
cat changelog.md >> $GITHUB_STEP_SUMMARY
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.validate-version.outputs.version }}
name: Release ${{ needs.validate-version.outputs.version }}
body_path: changelog.md
draft: false
prerelease: ${{ needs.validate-version.outputs.is_prerelease == 'true' }}
generate_release_notes: true
files: |
package.json
package-lock.json
- name: Update latest release badge
if: needs.validate-version.outputs.is_prerelease == 'false'
run: |
echo "Latest stable release: ${{ needs.validate-version.outputs.version }}"
notify-success:
name: Notify Success
needs: [validate-version, create-release]
runs-on: ubuntu-latest
if: success()
steps:
- name: Send success notification
run: |
echo "✅ Release ${{ needs.validate-version.outputs.version }} completed successfully!"
echo "- Docker image: ghcr.io/${{ github.repository }}:${{ needs.validate-version.outputs.version }}"
echo "- GitHub Release: https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate-version.outputs.version }}"