Skip to content

Release

Release #13

Workflow file for this run

name: Release & Publish
on:
# Only run on main branch after CI passes
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
# Manual trigger for emergency releases
workflow_dispatch:
inputs:
release_type:
description: 'Release type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
- prerelease
permissions:
contents: write
packages: write
id-token: write
actions: read
jobs:
check-ci:
name: Check CI Status
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
outputs:
should_release: ${{ steps.check.outputs.should_release }}
steps:
- name: Check CI passed
id: check
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "Manual release triggered"
echo "should_release=true" >> $GITHUB_OUTPUT
elif [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then
echo "CI passed, checking for release commits"
echo "should_release=true" >> $GITHUB_OUTPUT
else
echo "CI failed, skipping release"
echo "should_release=false" >> $GITHUB_OUTPUT
exit 1
fi
release:
name: Release & Publish
needs: check-ci
if: needs.check-ci.outputs.should_release == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- name: Cache node_modules
uses: actions/cache@v4
id: npm-cache
with:
path: node_modules
key: ${{ runner.os }}-node-20.x-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-20.x-
- name: Install dependencies
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Determine version bump
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "bump_type=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT
else
# Check commit messages for conventional commits
COMMITS=$(git log $(git describe --tags --abbrev=0 2>/dev/null || echo '')..HEAD --pretty=format:"%s" 2>/dev/null || git log --pretty=format:"%s")
# Check if this is the first release (no tags exist)
if ! git describe --tags --abbrev=0 2>/dev/null; then
echo "First release detected"
echo "bump_type=patch" >> $GITHUB_OUTPUT
elif echo "$COMMITS" | grep -qE "^(feat|fix)!:|BREAKING CHANGE:"; then
echo "bump_type=major" >> $GITHUB_OUTPUT
elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
echo "bump_type=minor" >> $GITHUB_OUTPUT
elif echo "$COMMITS" | grep -qE "^fix(\(.+\))?:"; then
echo "bump_type=patch" >> $GITHUB_OUTPUT
else
# For workflow_run events, default to patch if no conventional commits
if [ "${{ github.event_name }}" == "workflow_run" ]; then
echo "No conventional commits found, defaulting to patch"
echo "bump_type=patch" >> $GITHUB_OUTPUT
else
echo "No version bump needed"
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
fi
fi
- name: Check if should publish
id: check
if: steps.version.outputs.skip != 'true'
run: |
# Check if package exists on NPM
if npm view @nexcraft/forge version 2>/dev/null; then
echo "Package exists on NPM"
CURRENT_VERSION=$(npm view @nexcraft/forge version)
echo "current_npm_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
else
echo "Package does not exist on NPM yet"
echo "first_publish=true" >> $GITHUB_OUTPUT
fi
- name: Build library
if: steps.version.outputs.skip != 'true'
run: npm run build
- name: Bump version
id: bump
if: steps.version.outputs.skip != 'true'
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
# For first publish, use current version if it's already set
if [ "${{ steps.check.outputs.first_publish }}" == "true" ]; then
if [ "$CURRENT_VERSION" == "0.0.0" ]; then
npm version 0.0.1 --no-git-tag-version --allow-same-version
else
echo "Using existing version $CURRENT_VERSION for first publish"
fi
else
npm version ${{ steps.version.outputs.bump_type }} --no-git-tag-version
fi
VERSION=$(node -p "require('./package.json').version")
echo "new_version=$VERSION" >> $GITHUB_OUTPUT
- name: Generate changelog entry
if: steps.version.outputs.skip != 'true'
run: |
VERSION="${{ steps.bump.outputs.new_version }}"
DATE=$(date +%Y-%m-%d)
# Create changelog entry
cat > CHANGELOG_ENTRY.md << EOF
## v$VERSION - $DATE
### Changes
EOF
# Add commit messages
git log $(git describe --tags --abbrev=0 2>/dev/null || echo '')..HEAD --pretty=format:"- %s" >> CHANGELOG_ENTRY.md || echo "- Initial release" >> CHANGELOG_ENTRY.md
# Prepend to CHANGELOG.md (after the header)
if [ -f CHANGELOG.md ]; then
head -n 2 CHANGELOG.md > CHANGELOG_NEW.md
cat CHANGELOG_ENTRY.md >> CHANGELOG_NEW.md
echo "" >> CHANGELOG_NEW.md
tail -n +3 CHANGELOG.md >> CHANGELOG_NEW.md
mv CHANGELOG_NEW.md CHANGELOG.md
else
echo "# Changelog" > CHANGELOG.md
echo "" >> CHANGELOG.md
cat CHANGELOG_ENTRY.md >> CHANGELOG.md
fi
rm CHANGELOG_ENTRY.md
- name: Commit version bump
if: steps.version.outputs.skip != 'true'
run: |
git add package.json package-lock.json CHANGELOG.md
git commit -m "chore(release): v${{ steps.bump.outputs.new_version }} [skip ci]" || echo "No changes to commit"
- name: Create git tag
if: steps.version.outputs.skip != 'true'
run: |
if ! git rev-parse v${{ steps.bump.outputs.new_version }} >/dev/null 2>&1; then
git tag -a v${{ steps.bump.outputs.new_version }} -m "Release v${{ steps.bump.outputs.new_version }}"
else
echo "Tag v${{ steps.bump.outputs.new_version }} already exists, skipping tag creation"
fi
- name: Push changes
if: steps.version.outputs.skip != 'true'
run: |
git push origin main --follow-tags
- name: Publish to NPM
if: steps.version.outputs.skip != 'true'
run: |
# Dry run first to check
npm publish --dry-run
# Actual publish
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
if: steps.version.outputs.skip != 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.bump.outputs.new_version }}
name: Release v${{ steps.bump.outputs.new_version }}
body_path: CHANGELOG.md
draft: false
prerelease: ${{ steps.version.outputs.bump_type == 'prerelease' }}
files: |
dist/nexcraft-forge.es.js
dist/nexcraft-forge.umd.js
dist/index.d.ts
publish-docs:
name: Publish Documentation
needs: release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main # Get the latest including version bump
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Cache node_modules
uses: actions/cache@v4
id: npm-cache
with:
path: node_modules
key: ${{ runner.os }}-node-20.x-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-20.x-
- name: Install dependencies
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
- name: Build Storybook
run: npm run build-storybook
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./storybook-static
cname: forge.ignis.dev # Optional: custom domain