feat(audit): add --badge flag and GitHub Action for CI/CD #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Release | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: 'Release type' | |
| required: true | |
| default: 'patch' | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| channel: | |
| description: 'Release channel' | |
| required: true | |
| default: 'stable' | |
| type: choice | |
| options: | |
| - stable | |
| - beta | |
| - alpha | |
| permissions: | |
| contents: write | |
| id-token: write | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| NODE_VERSION: '20' | |
| jobs: | |
| detect-release: | |
| name: Detect Release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_release: ${{ steps.check.outputs.should_release }} | |
| release_type: ${{ steps.check.outputs.release_type }} | |
| channel: ${{ steps.check.outputs.channel }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - name: Check for release commit | |
| id: check | |
| run: | | |
| # Manual workflow dispatch | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "should_release=true" >> $GITHUB_OUTPUT | |
| echo "release_type=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT | |
| echo "channel=${{ github.event.inputs.channel }}" >> $GITHUB_OUTPUT | |
| echo "Manual release triggered: ${{ github.event.inputs.release_type }} (${{ github.event.inputs.channel }})" | |
| exit 0 | |
| fi | |
| COMMIT_MSG=$(git log -1 --pretty=%B) | |
| echo "Commit: $COMMIT_MSG" | |
| # Skip for version bumps and dependency updates | |
| if [[ "$COMMIT_MSG" =~ \[skip\ ci\] ]] || \ | |
| [[ "$COMMIT_MSG" =~ ^chore:\ bump\ version ]] || \ | |
| [[ "$COMMIT_MSG" =~ ^chore\(deps ]] || \ | |
| [[ "$COMMIT_MSG" =~ ^Merge\ pull\ request.*dependabot ]]; then | |
| echo "should_release=false" >> $GITHUB_OUTPUT | |
| echo "Skipping: maintenance commit" | |
| exit 0 | |
| fi | |
| # Use sync-versions.js for semantic detection | |
| RELEASE_OUTPUT=$(node scripts/sync-versions.js detect 2>&1) | |
| if echo "$RELEASE_OUTPUT" | grep -q "should_release=true"; then | |
| echo "$RELEASE_OUTPUT" >> $GITHUB_OUTPUT | |
| echo "Release detected from commit" | |
| else | |
| echo "should_release=false" >> $GITHUB_OUTPUT | |
| echo "No release pattern in commit" | |
| fi | |
| bump-version: | |
| name: Bump Version | |
| runs-on: ubuntu-latest | |
| needs: detect-release | |
| if: needs.detect-release.outputs.should_release == 'true' | |
| outputs: | |
| new_version: ${{ steps.bump.outputs.new_version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Bump version | |
| id: bump | |
| run: | | |
| NEW_VERSION=$(node scripts/sync-versions.js release ${{ needs.detect-release.outputs.release_type }} ${{ needs.detect-release.outputs.channel }}) | |
| if [ -z "$NEW_VERSION" ]; then | |
| echo "Failed to bump version" | |
| exit 1 | |
| fi | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "Bumped to: $NEW_VERSION" | |
| - name: Commit version bump | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| git add packages/*/package.json | |
| if ! git diff --staged --quiet; then | |
| git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }} [skip ci]" | |
| git push || echo "Push blocked by branch rules, continuing with local bump" | |
| else | |
| echo "No changes to commit" | |
| fi | |
| validate: | |
| name: Validate | |
| runs-on: ubuntu-latest | |
| needs: [detect-release, bump-version] | |
| if: needs.detect-release.outputs.should_release == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - name: Re-apply version bump | |
| run: node scripts/sync-versions.js release ${{ needs.detect-release.outputs.release_type }} ${{ needs.detect-release.outputs.channel }} > /dev/null | |
| - run: npm ci | |
| - run: npx turbo run typecheck | |
| - run: npx turbo run test | |
| - run: npx turbo run build | |
| publish: | |
| name: Publish to npm | |
| needs: [detect-release, bump-version, validate] | |
| if: needs.detect-release.outputs.should_release == 'true' && needs.validate.result == 'success' | |
| runs-on: ubuntu-latest | |
| environment: npm-publish | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| registry-url: https://registry.npmjs.org | |
| - name: Upgrade npm for OIDC support | |
| run: npm install -g npm@latest | |
| - name: Re-apply version bump | |
| run: node scripts/sync-versions.js release ${{ needs.detect-release.outputs.release_type }} ${{ needs.detect-release.outputs.channel }} > /dev/null | |
| - run: npm ci | |
| - run: npx turbo run build | |
| - name: Publish @glincker/geo-audit | |
| working-directory: packages/geo-audit | |
| run: | | |
| VERSION=${{ needs.bump-version.outputs.new_version }} | |
| if npm view @glincker/geo-audit@$VERSION version 2>/dev/null; then | |
| echo "Version $VERSION already exists, skipping" | |
| else | |
| npm publish --provenance --access public | |
| fi | |
| - name: Publish @glincker/geo-seo | |
| working-directory: packages/geo-seo | |
| run: | | |
| VERSION=${{ needs.bump-version.outputs.new_version }} | |
| if npm view @glincker/geo-seo@$VERSION version 2>/dev/null; then | |
| echo "Version $VERSION already exists, skipping" | |
| else | |
| npm publish --provenance --access public | |
| fi | |
| - name: Publish @glincker/geomark | |
| working-directory: packages/geomark | |
| run: | | |
| VERSION=${{ needs.bump-version.outputs.new_version }} | |
| if npm view @glincker/geomark@$VERSION version 2>/dev/null; then | |
| echo "Version $VERSION already exists, skipping" | |
| else | |
| npm publish --provenance --access public | |
| fi | |
| - name: Publish @glincker/geokit | |
| working-directory: packages/geokit | |
| run: | | |
| VERSION=${{ needs.bump-version.outputs.new_version }} | |
| if npm view @glincker/geokit@$VERSION version 2>/dev/null; then | |
| echo "Version $VERSION already exists, skipping" | |
| else | |
| npm publish --provenance --access public | |
| fi | |
| github-release: | |
| name: GitHub Release | |
| needs: [detect-release, bump-version, publish] | |
| if: needs.detect-release.outputs.should_release == 'true' && needs.publish.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.ref }} | |
| fetch-depth: 0 | |
| - name: Create git tag | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| TAG_NAME="v${{ needs.bump-version.outputs.new_version }}" | |
| if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "Tag $TAG_NAME already exists, skipping" | |
| else | |
| git tag "$TAG_NAME" | |
| git push origin "$TAG_NAME" | |
| echo "Created tag $TAG_NAME" | |
| fi | |
| - name: Generate release notes | |
| id: notes | |
| run: | | |
| VERSION="${{ needs.bump-version.outputs.new_version }}" | |
| CHANNEL="${{ needs.detect-release.outputs.channel }}" | |
| # Get changelog from commits since last tag | |
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| if [ -n "$PREV_TAG" ]; then | |
| CHANGELOG=$(git log --pretty=format:"- %s" ${PREV_TAG}..HEAD | grep -E "^- (feat|fix|perf|refactor|revert)" | head -15 || echo "- Version bump") | |
| else | |
| CHANGELOG="- Initial release" | |
| fi | |
| cat > release_notes.md << EOF | |
| ## v${VERSION} | |
| ### Installation | |
| \`\`\`bash | |
| npm install @glincker/geokit@${VERSION} | |
| # Or individual packages: | |
| npm install @glincker/geo-audit@${VERSION} | |
| npm install @glincker/geo-seo@${VERSION} | |
| npm install @glincker/geomark@${VERSION} | |
| \`\`\` | |
| ### Changes | |
| ${CHANGELOG} | |
| ### Packages | |
| | Package | Version | | |
| |---------|---------| | |
| | @glincker/geo-audit | ${VERSION} | | |
| | @glincker/geo-seo | ${VERSION} | | |
| | @glincker/geomark | ${VERSION} | | |
| | @glincker/geokit | ${VERSION} | | |
| --- | |
| *Auto-released from conventional commit* | |
| EOF | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.bump-version.outputs.new_version }} | |
| name: v${{ needs.bump-version.outputs.new_version }} | |
| body_path: release_notes.md | |
| draft: false | |
| prerelease: ${{ needs.detect-release.outputs.channel != 'stable' }} | |
| summary: | |
| name: Release Summary | |
| runs-on: ubuntu-latest | |
| needs: [detect-release, bump-version, validate, publish, github-release] | |
| if: always() && needs.detect-release.outputs.should_release == 'true' | |
| steps: | |
| - name: Summary | |
| run: | | |
| echo "## Release Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Version:** v${{ needs.bump-version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Type:** ${{ needs.detect-release.outputs.release_type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Channel:** ${{ needs.detect-release.outputs.channel }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|------|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Validate | ${{ needs.validate.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Publish | ${{ needs.publish.result }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| GitHub Release | ${{ needs.github-release.result }} |" >> $GITHUB_STEP_SUMMARY |