Skip to content

Implement lino-arguments library prototype #3

Implement lino-arguments library prototype

Implement lino-arguments library prototype #3

Workflow file for this run

name: CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
# Changeset check - only runs on PRs
changeset-check:
name: Check for Changesets
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: npm install
- name: Check for changesets
run: |
# Skip changeset check for automated version PRs
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]]; then
echo "Skipping changeset check for automated release PR"
exit 0
fi
# Count changeset files (excluding README.md and config.json)
CHANGESET_COUNT=$(find .changeset -name "*.md" ! -name "README.md" | wc -l)
echo "Found $CHANGESET_COUNT changeset file(s)"
# Ensure exactly one changeset file exists
if [ "$CHANGESET_COUNT" -eq 0 ]; then
echo "::error::No changeset found. Please add a changeset by running 'npm run changeset' and commit the result."
exit 1
elif [ "$CHANGESET_COUNT" -gt 1 ]; then
echo "::error::Multiple changesets found ($CHANGESET_COUNT). Each PR should have exactly ONE changeset."
echo "::error::Found changeset files:"
find .changeset -name "*.md" ! -name "README.md" -exec basename {} \;
exit 1
fi
# Get the changeset file
CHANGESET_FILE=$(find .changeset -name "*.md" ! -name "README.md" | head -1)
echo "Validating changeset: $CHANGESET_FILE"
# Check if changeset has a valid type (major, minor, or patch)
if ! grep -qE "^['\"]lino-arguments['\"]:\s+(major|minor|patch)" "$CHANGESET_FILE"; then
echo "::error::Changeset must specify a version type: major, minor, or patch"
echo "::error::Expected format in $CHANGESET_FILE:"
echo "::error::---"
echo "::error::'lino-arguments': patch"
echo "::error::---"
echo "::error::"
echo "::error::Your description here"
cat "$CHANGESET_FILE"
exit 1
fi
# Extract description (everything after the closing ---) and check it's not empty
DESCRIPTION=$(awk '/^---$/{count++; next} count==2' "$CHANGESET_FILE" | sed '/^[[:space:]]*$/d')
if [ -z "$DESCRIPTION" ]; then
echo "::error::Changeset must include a description of the changes"
echo "::error::The description should appear after the closing '---' in the changeset file"
echo "::error::Current content of $CHANGESET_FILE:"
cat "$CHANGESET_FILE"
exit 1
fi
echo "✅ Changeset validation passed"
echo " Type: $(grep -E "^['\"]lino-arguments['\"]:" "$CHANGESET_FILE" | sed "s/.*: //")"
echo " Description: $DESCRIPTION"
# Linting and formatting - runs after changeset check on PRs, immediately on main
lint:
name: Lint and Format Check
runs-on: ubuntu-latest
needs: [changeset-check]
if: always() && (github.event_name == 'push' || needs.changeset-check.result == 'success')
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: npm install
- name: Run ESLint
run: npm run lint
- name: Check formatting
run: npm run format:check
- name: Check file size limit
run: npm run check:file-size
# Test on Node.js - runs after changeset check on PRs, immediately on main
test-node:
name: Test on Node.js
runs-on: ubuntu-latest
needs: [changeset-check]
if: always() && (github.event_name == 'push' || needs.changeset-check.result == 'success')
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
# Test on Bun - runs after changeset check on PRs, immediately on main
test-bun:
name: Test on Bun
runs-on: ubuntu-latest
needs: [changeset-check]
if: always() && (github.event_name == 'push' || needs.changeset-check.result == 'success')
steps:
- uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Run tests
run: bun test
# Test on Deno - runs after changeset check on PRs, immediately on main
test-deno:
name: Test on Deno
runs-on: ubuntu-latest
needs: [changeset-check]
if: always() && (github.event_name == 'push' || needs.changeset-check.result == 'success')
steps:
- uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Run tests
run: deno test --allow-read
# Release - only runs on main after tests pass
release:
name: Release
runs-on: ubuntu-latest
needs: [lint, test-node, test-bun, test-deno]
# Use always() to ensure this job runs even if changeset-check was skipped
# This is needed because lint/test jobs have a transitive dependency on changeset-check
if: always() && github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.lint.result == 'success' && needs.test-node.result == 'success' && needs.test-bun.result == 'success' && needs.test-deno.result == 'success'
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install
- name: Check for changesets
id: check_changesets
run: |
# Count changeset files (excluding README.md and config.json)
CHANGESET_COUNT=$(find .changeset -name "*.md" ! -name "README.md" | wc -l)
echo "Found $CHANGESET_COUNT changeset file(s)"
echo "has_changesets=$([[ $CHANGESET_COUNT -gt 0 ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
- name: Version packages and commit to main
if: steps.check_changesets.outputs.has_changesets == 'true'
id: version
run: |
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Get current version before bump
OLD_VERSION=$(node -p "require('./package.json').version")
echo "Current version: $OLD_VERSION"
# Run changeset version to bump versions and update CHANGELOG
npm run changeset:version
# Get new version after bump
NEW_VERSION=$(node -p "require('./package.json').version")
echo "New version: $NEW_VERSION"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
# Check if there are changes to commit
if [[ -n $(git status --porcelain) ]]; then
echo "Changes detected, committing..."
# Stage all changes (package.json, package-lock.json, CHANGELOG.md, deleted changesets)
git add -A
# Commit with version number as message
git commit -m "$NEW_VERSION" \
-m "" \
-m "🤖 Generated with [Claude Code](https://claude.com/claude-code)"
# Push directly to main
git push origin main
echo "✅ Version bump committed and pushed to main"
echo "version_committed=true" >> $GITHUB_OUTPUT
else
echo "No changes to commit"
echo "version_committed=false" >> $GITHUB_OUTPUT
fi
- name: Publish to npm
if: steps.version.outputs.version_committed == 'true'
id: publish
run: |
# Pull the latest changes we just pushed
git pull origin main
# Publish to npm
npm run changeset:publish
echo "published=true" >> $GITHUB_OUTPUT
# Get published version
PUBLISHED_VERSION=$(node -p "require('./package.json').version")
echo "published_version=$PUBLISHED_VERSION" >> $GITHUB_OUTPUT
echo "✅ Published lino-arguments@$PUBLISHED_VERSION to npm"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
if: steps.publish.outputs.published == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ steps.publish.outputs.published_version }}"
TAG="v$VERSION"
echo "Creating GitHub release for $TAG..."
# Extract changelog entry for this version
# Read from CHANGELOG.md between this version and the next version marker
RELEASE_NOTES=$(awk "/## $VERSION/,/## [0-9]/" CHANGELOG.md | sed '1d;$d' | sed '/^$/d')
if [ -z "$RELEASE_NOTES" ]; then
RELEASE_NOTES="Release $VERSION"
fi
# Create release
gh release create "$TAG" \
--title "$VERSION" \
--notes "$RELEASE_NOTES" \
--repo ${{ github.repository }}
echo "✅ Created GitHub release: $TAG"
- name: Format GitHub release notes
if: steps.publish.outputs.published == 'true'
run: |
VERSION="${{ steps.publish.outputs.published_version }}"
TAG="v$VERSION"
# Get the release ID for this version
RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/$TAG --jq '.id' 2>/dev/null || echo "")
if [ -n "$RELEASE_ID" ]; then
echo "Formatting release notes for $TAG..."
node scripts/format-release-notes.mjs "$RELEASE_ID" "$TAG" "${{ github.repository }}"
echo "✅ Formatted release notes for $TAG"
else
echo "⚠️ Could not find release for $TAG"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}