name: Publish to npm
on:
# Trigger on PR merge to main
push:
branches: [ main ]
# Optionally trigger on tags
# tags:
# - 'v*'
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: write # Needed for pushing commits and tags
pull-requests: read # Needed for reading PR information
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
# Fetch enough history for version bump determination
fetch-depth: 50
ref: ${{ github.ref }}
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
env:
ESLINT_MAX_WARNINGS: 0
- name: Test
run: npm test
- name: Build
run: npm run build
- name: Configure Git
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
- name: Determine version bump type
id: version-bump
run: |
# Enable bash debugging
set -x
# Get the merge commit message (PR title)
PR_TITLE=$(git log -1 --pretty=%s)
echo "PR Title: $PR_TITLE"
# Get all commit messages from the PR
echo "Analyzing recent commit messages (if available)..."
# Try to get commit messages, but don't fail if we can't
COMMIT_MESSAGES=""
if git rev-parse HEAD~10 >/dev/null 2>&1; then
COMMIT_MESSAGES=$(git log HEAD~10..HEAD --format=%s)
else
# If we can't get 10 commits back, try with fewer
for i in {5..1}; do
if git rev-parse HEAD~$i >/dev/null 2>&1; then
COMMIT_MESSAGES=$(git log HEAD~$i..HEAD --format=%s)
echo "Found $i commits to analyze"
break
fi
done
fi
# If we still don't have any commit messages, just use the PR title
if [ -z "$COMMIT_MESSAGES" ]; then
echo "No commit history available, using only PR title for analysis"
COMMIT_MESSAGES="$PR_TITLE"
fi
# Initialize bump type as patch (default)
BUMP_TYPE="patch"
# Function to check for conventional commit prefixes
check_for_prefixes() {
local message="$1"
# Skip GitHub's automated merge commit messages
if [[ "$message" =~ ^Merge\ pull\ request\ #[0-9]+\ from\ .* ]]; then
echo "Detected GitHub merge commit message, skipping: $message"
# Don't change BUMP_TYPE for merge commits, just return success
return 0
fi
# Continue with conventional commit checking
# Check for breaking changes (highest priority)
if [[ "$message" == *"BREAKING CHANGE"* ]] || [[ "$message" == *"!"* ]]; then
echo "Detected breaking change - using major version bump"
BUMP_TYPE="major"
FOUND_CONVENTIONAL_COMMIT=true
return 0
fi
# Check for features (medium priority)
if [[ "$message" == "feat"* ]] || [[ "$message" == "feat:"* ]] || [[ "$message" =~ "feat\(" ]]; then
# Only upgrade to minor if we haven't already decided on major
if [[ "$BUMP_TYPE" != "major" ]]; then
echo "Detected new feature - using minor version bump"
BUMP_TYPE="minor"
FOUND_CONVENTIONAL_COMMIT=true
fi
return 0
fi
# Check for fixes (lowest priority)
if [[ "$message" == "fix"* ]] || [[ "$message" == "fix:"* ]] || [[ "$message" =~ "fix\(" ]]; then
# Only set to patch if we haven't decided on minor or major
if [[ "$BUMP_TYPE" != "major" ]] && [[ "$BUMP_TYPE" != "minor" ]]; then
echo "Detected bug fix - using patch version bump"
BUMP_TYPE="patch"
FOUND_CONVENTIONAL_COMMIT=true
fi
return 0
fi
# For non-merge commits that don't match conventions, we should report an error
echo "WARNING: No conventional commit prefix found in: $message"
# Instead of returning an error, we'll use the default patch version
echo "Using default patch version bump"
return 0
}
# First check the PR title (highest priority)
check_for_prefixes "$PR_TITLE"
# If the check failed but it's not a merge commit, we need to handle it
PR_CHECK_RESULT=$?
if [[ $PR_CHECK_RESULT -ne 0 ]]; then
echo "PR title doesn't follow conventional commit format and is not a merge commit"
echo "Will still check individual commits for version bump determination"
fi
# Save the current BUMP_TYPE after checking PR title
echo "After PR title check: BUMP_TYPE=$BUMP_TYPE"
# Process commit messages one by one
echo "Processing commit messages:"
# Set error handling to continue even if a command fails
set +e
echo "$COMMIT_MESSAGES" > /tmp/commit_messages.txt
FOUND_CONVENTIONAL_COMMIT=false
while IFS= read -r message; do
echo "Processing message: $message"
if [[ -n "$message" ]]; then
check_for_prefixes "$message"
echo "After checking message: BUMP_TYPE=$BUMP_TYPE"
fi
done < /tmp/commit_messages.txt
# Restore error handling
set -e
echo "BUMP_TYPE after processing all messages: $BUMP_TYPE"
# Check if we found at least one conventional commit
if [[ "$BUMP_TYPE" == "patch" ]] && [[ "$FOUND_CONVENTIONAL_COMMIT" == "false" ]]; then
echo "WARNING: No conventional commits found in this PR"
echo "Using default patch version bump"
else
echo "Found conventional commits, using determined version bump: $BUMP_TYPE"
fi
# Output the final decision
echo "Final decision: $BUMP_TYPE version bump"
# Set the output in a way that works with both older and newer GitHub Actions
if [ -n "$GITHUB_OUTPUT" ]; then
# New way (GitHub Actions runner >= 2.297.0)
echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT
else
# Old way (GitHub Actions runner < 2.297.0)
echo "::set-output name=bump_type::$BUMP_TYPE"
fi
- name: Bump version
run: |
echo "Bumping version: ${{ steps.version-bump.outputs.bump_type }}"
# Debug output
echo "Version bump type from previous step: ${{ steps.version-bump.outputs.bump_type }}"
# Set up git user
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
# Temporarily disable husky hooks for CI
echo "Temporarily disabling husky hooks for CI..."
export HUSKY=0
# Use npm version to bump version, create commit and tag
# Use a conventional commit format for the version bump commit
npm version ${{ steps.version-bump.outputs.bump_type }} -m "chore(release): bump version to %s [skip ci]"
# Note: We're using both HUSKY=0 and a conventional commit format for maximum compatibility
# This ensures the commit passes even if commitlint is still somehow triggered
# Push changes back to the repository with better error handling
echo "Pushing changes to repository..."
# Set the remote URL with token
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
# Push changes and tags
git push origin HEAD:main
git push origin --tags
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}