diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 183b05eb..792cac35 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -7,6 +7,10 @@ on: description: 'Version number (e.g., 2.3.0 or 2.3.0-beta1)' required: true type: string + ticket: + description: 'Jira ticket number (e.g., 1234 for SDK-1234)' + required: true + type: string permissions: contents: write @@ -16,70 +20,62 @@ jobs: prepare-release: runs-on: ubuntu-latest steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} + private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} + ref: main + token: ${{ steps.app-token.outputs.token }} + + - name: Validate version format + run: | + if ! [[ "${{ github.event.inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then + echo "::error::Invalid version format. Use semantic versioning (e.g., 2.3.0 or 2.3.0-beta1)" + exit 1 + fi - name: Update Changelog id: update_changelog run: | + version="${{ github.event.inputs.version }}" changelog_file="CHANGELOG.md" - # Function to extract content between two patterns, including the first pattern - extract_between() { - awk "/^## \[$1\]/{p=1;print;next} /^## \[/{p=0} p" "$3" - } - - # Get the unreleased content - unreleased_content=$(extract_between "Unreleased" "[0-9]" "$changelog_file") + unreleased_content=$(awk '/^## \[Unreleased\]/{p=1;next} /^## \[/{p=0} p' "$changelog_file") if [ -z "$unreleased_content" ]; then - echo "No unreleased changes found in $changelog_file" + echo "::error::No unreleased changes found in $changelog_file. Add release notes to the [Unreleased] section before running this workflow." exit 1 fi - # Get the current version - current_version=$(grep -oP "^## \[\K[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9]+)?(?=\])" "$changelog_file" | head -n1) - new_version="${{ github.event.inputs.version }}" + notes_file="$RUNNER_TEMP/release-notes.md" + printf '%s' "$unreleased_content" > "$notes_file" - # Validate version format (includes beta versions) - if ! [[ $new_version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$ ]]; then - echo "Invalid version format. Please use semantic versioning (e.g., 2.3.0 or 2.3.0-beta1)" - exit 1 - fi - - echo "new_version=${new_version}" >> $GITHUB_OUTPUT - - # Create temporary file temp_file=$(mktemp) - - # Preserve header and write new content { - # Preserve the header (first 4 lines) head -n 4 "$changelog_file" echo "## [Unreleased]" echo "" - echo "## [$new_version]" - # Drop the leading "## [Unreleased]" header from the extracted content + echo "## [$version]" echo "$unreleased_content" | tail -n +2 echo "" - # Get the rest of the file starting from the first version entry sed -n '/^## \[[0-9]/,$p' "$changelog_file" } > "$temp_file" - - # Replace original file mv "$temp_file" "$changelog_file" - - name: Update Version + - name: Bump package.json version run: npm --no-git-tag-version --allow-same-version version "${{ github.event.inputs.version }}" - name: Create Pull Request uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 # v5 with: - token: ${{ secrets.GITHUB_TOKEN }} - title: "Prepare for Release ${{ steps.update_changelog.outputs.new_version }}" + token: ${{ steps.app-token.outputs.token }} + title: "SDK-${{ github.event.inputs.ticket }}: Prepare for Release ${{ github.event.inputs.version }}" body: | - # Prepare for Release ${{ steps.update_changelog.outputs.new_version }} + # Prepare for Release ${{ github.event.inputs.version }} ## SDK Release Checklist - [ ] CHANGELOG.md updated (Unreleased section moved under the new version) @@ -87,8 +83,23 @@ jobs: - [ ] README.md reviewed (if needed) - [ ] All tests passing - [ ] Documentation updated (if needed) - - branch: "prepare-for-release-${{ steps.update_changelog.outputs.new_version }}" - commit-message: "Prepare for release ${{ steps.update_changelog.outputs.new_version }}" + branch: "release/SDK-${{ github.event.inputs.ticket }}-${{ github.event.inputs.version }}" + commit-message: "[SDK-${{ github.event.inputs.ticket }}]: Prepare for release ${{ github.event.inputs.version }}" labels: release delete-branch: true + + - name: Create Draft GitHub Release + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + version="${{ github.event.inputs.version }}" + notes="${{ steps.update_changelog.outputs.notes }}" + + if gh release view "$version" &>/dev/null; then + echo "Draft release $version already exists, skipping." + else + gh release create "$version" \ + --draft \ + --title "$version" \ + --notes-file "$RUNNER_TEMP/release-notes.md" + fi diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 00000000..5c710e31 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,85 @@ +name: Publish Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to publish (e.g., 2.3.0)' + required: true + type: string + +env: + VERSION: ${{ github.event.inputs.version }} + +permissions: + contents: write + id-token: write + +jobs: + publish-release: + runs-on: ubuntu-latest + environment: npm + steps: + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ITERABLE_SDK_RELEASE_APP_ID }} + private-key: ${{ secrets.ITERABLE_SDK_RELEASE_APP_PRIVATE_KEY }} + + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: main + token: ${{ steps.app-token.outputs.token }} + + - name: Verify release is ready + run: | + if ! grep -qE "^## \[$VERSION\]" CHANGELOG.md; then + echo "::error::CHANGELOG.md has no entry for [$VERSION]. Merge the prepare-release PR to main before running this workflow." + exit 1 + fi + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '22' + cache: 'yarn' + registry-url: 'https://registry.npmjs.org' + + - name: Update npm for OIDC support + run: npm install -g npm@latest + + - run: yarn install --frozen-lockfile + + - run: yarn test + + - run: yarn build + + - run: yarn build:node + + - name: Publish to npm + run: npm publish --provenance + + - name: Publish draft GitHub release and tag main + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh release edit "$VERSION" \ + --draft=false \ + --target "$(git rev-parse HEAD)" \ + --latest + + - name: Slack notification + run: | + release_url="https://github.com/${{ github.repository }}/releases/tag/$VERSION" + payload=$(jq -n \ + --arg text ":javascript: *Web SDK ${VERSION}* has been released. <${release_url}|View release notes>." \ + '{"text": $text}') + curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" + + - name: Slack failure notification + if: failure() + run: | + run_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}" + payload=$(jq -n \ + --arg text ":alert: Web SDK release ${VERSION} failed (attempt ${{ github.run_attempt }}). Run: ${run_url}" \ + '{"text": $text}') + curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "${{ secrets.SLACK_WEBHOOK }}" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 593aabf2..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: publish -on: - release: - types: - - published - -jobs: - publish: - environment: npm - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Use Node.js 22 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: '22' - cache: 'yarn' - registry-url: 'https://registry.npmjs.org' - - name: Update npm for OIDC support - run: npm install -g npm@latest - - run: yarn install --frozen-lockfile - - run: yarn test - - run: yarn build - - run: yarn build:node - - name: Publish to npm - run: npm publish --provenance