diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml new file mode 100644 index 0000000..9568cef --- /dev/null +++ b/.github/workflows/create-release-pr.yml @@ -0,0 +1,76 @@ +name: Create Release PR + +on: + workflow_dispatch: + inputs: + version: + description: 'Version type' + required: true + type: choice + options: + - patch + - minor + - major + +jobs: + create-release-pr: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 + with: + node-version: 22 + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Version bump + run: | + npm version ${{ github.event.inputs.version }} --no-git-tag-version + VERSION=$(node -p "require('./package.json').version") + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Get release notes + id: release-notes + run: | + # Get the last tag + LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + if [ -z "$LAST_TAG" ]; then + LAST_TAG=$(git rev-list --max-parents=0 HEAD) + fi + + # Generate release notes + NOTES=$(gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/${{ github.repository }}/releases/generate-notes \ + -f tag_name="v${{ env.VERSION }}" \ + -f target_commitish="master" \ + -f previous_tag_name="${LAST_TAG}" | jq -r '.body') + + # Save to file to handle multiline content + echo "$NOTES" > release-notes.md + env: + GH_TOKEN: ${{ github.token }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 + with: + branch: release/v${{ env.VERSION }} + delete-branch: true + title: "Release v${{ env.VERSION }}" + body-path: release-notes.md + commit-message: "chore: release v${{ env.VERSION }}" + labels: | + Type: Release + assignees: ${{ github.actor }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b9e6d9b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,134 @@ +name: Release + +on: + pull_request: + branches: + - master + - main + types: + - closed + workflow_dispatch: + inputs: + version: + description: 'Version to publish (e.g., 1.2.3)' + required: false + type: string + +jobs: + release: + if: | + (github.event_name == 'pull_request' && + github.event.pull_request.merged == true && + contains(github.event.pull_request.labels.*.name, 'Type: Release')) || + github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write # OIDC + pull-requests: write # PR comment + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Get package info + id: package + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION=$(node -p "require('./package.json').version") + fi + PACKAGE_NAME=$(node -p "require('./package.json').name") + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "name=$PACKAGE_NAME" >> $GITHUB_OUTPUT + + - name: Check if tag exists + id: tag-check + run: | + if git rev-parse "v${{ steps.package.outputs.version }}" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + fi + + - name: Setup Node.js + if: steps.tag-check.outputs.exists == 'false' + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 + with: + node-version: 22 + registry-url: 'https://registry.npmjs.org' + + - name: Ensure npm 11.5.1 or later is installed + if: steps.tag-check.outputs.exists == 'false' + run: | + NPM_VERSION=$(npm -v) + echo "Current npm version: $NPM_VERSION" + if ! npx semver -r ">=11.5.1" "$NPM_VERSION"; then + echo "npm version $NPM_VERSION is too old. Installing latest npm..." + npm install -g npm@latest + echo "Updated npm version: $(npm -v)" + fi + + - name: Install dependencies + if: steps.tag-check.outputs.exists == 'false' + run: npm ci + + - name: Build package + if: steps.tag-check.outputs.exists == 'false' + run: npm run build + + - name: Publish to npm with provenance + if: steps.tag-check.outputs.exists == 'false' + run: npm publish --provenance --access public + + - name: Create GitHub Release with tag + id: create-release + if: steps.tag-check.outputs.exists == 'false' + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + RELEASE_URL=$(gh release create "v${{ steps.package.outputs.version }}" \ + --title "v${{ steps.package.outputs.version }}" \ + --target "${{ github.sha }}" \ + --generate-notes) + else + RELEASE_URL=$(gh release create "v${{ steps.package.outputs.version }}" \ + --title "v${{ steps.package.outputs.version }}" \ + --target "${{ github.sha }}" \ + --notes "${{ github.event.pull_request.body }}") + fi + echo "url=$RELEASE_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Comment on PR - Success + if: | + always() && + github.event_name == 'pull_request' && + steps.tag-check.outputs.exists == 'false' && + success() + run: | + gh pr comment ${{ github.event.pull_request.number }} \ + --body "✅ **Release v${{ steps.package.outputs.version }} completed successfully!** + + - 📦 npm package: https://www.npmjs.com/package/${{ steps.package.outputs.name }}/v/${{ steps.package.outputs.version }} + - 🏷️ GitHub Release: ${{ steps.create-release.outputs.url }} + - 🔗 Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + GH_TOKEN: ${{ github.token }} + + - name: Comment on PR - Failure + if: | + always() && + github.event_name == 'pull_request' && + steps.tag-check.outputs.exists == 'false' && + failure() + run: | + gh pr comment ${{ github.event.pull_request.number }} \ + --body "❌ **Release v${{ steps.package.outputs.version }} failed** + + Please check the workflow logs for details. + 🔗 Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + GH_TOKEN: ${{ github.token }} \ No newline at end of file