diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d627e58 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,217 @@ +name: "Release" + +on: + pull_request: + types: [closed] + branches: [master] + +permissions: + contents: read + +# Concurrency control: only one release process can run at a time +# This prevents race conditions if multiple PRs with 'release' label merge simultaneously +concurrency: + group: release + cancel-in-progress: false + +jobs: + check-release-label: + name: Check for release label + runs-on: ubuntu-latest + # Run when PR with 'release' label is merged to master, or when manually triggered + if: | + github.event.pull_request.merged == true + && contains(github.event.pull_request.labels.*.name, 'release') + outputs: + should-release: ${{ steps.check.outputs.should-release }} + bump-type: ${{ steps.check.outputs.bump-type }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + + - name: Check release conditions + id: check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Determine bump type from PR labels + if ${{ contains(github.event.pull_request.labels.*.name, 'bump-major') }}; then + echo "bump-type=major" >> "$GITHUB_OUTPUT" + echo "should-release=true" >> "$GITHUB_OUTPUT" + elif ${{ contains(github.event.pull_request.labels.*.name, 'bump-minor') }}; then + echo "bump-type=minor" >> "$GITHUB_OUTPUT" + echo "should-release=true" >> "$GITHUB_OUTPUT" + elif ${{ contains(github.event.pull_request.labels.*.name, 'bump-patch') }}; then + echo "bump-type=patch" >> "$GITHUB_OUTPUT" + echo "should-release=true" >> "$GITHUB_OUTPUT" + fi + + notify-approval-needed: + name: Notify Slack - Approval Needed + needs: check-release-label + if: needs.check-release-label.outputs.should-release == 'true' + uses: posthog/.github/.github/workflows/notify-approval-needed.yml@main + with: + slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }} + slack_user_group_id: ${{ vars.GROUP_CLIENT_LIBRARIES_SLACK_GROUP_ID }} + secrets: + slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }} + posthog_project_api_key: ${{ secrets.POSTHOG_PROJECT_API_KEY }} + + release: + name: Bump versions and release + needs: [check-release-label, notify-approval-needed] + runs-on: ubuntu-latest + # Use `always()` to ensure the job runs even if the check-release-label job fails + # but still depend on it to be able to use `needs.notify-approval-needed.outputs.slack_ts` + if: always() && needs.check-release-label.outputs.should-release == 'true' + environment: "Release" # This will require an approval from a maintainer, they are notified in Slack above + permissions: + contents: write + actions: write + steps: + - name: Notify Slack - Approved + if: needs.notify-approval-needed.outputs.slack_ts != '' + uses: posthog/.github/.github/actions/slack-thread-reply@main + with: + slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }} + slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }} + thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }} + message: "✅ Release approved! Version bump in progress..." + emoji_reaction: "white_check_mark" + + - name: Get GitHub App token + id: releaser + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.GH_APP_POSTHOG_PHP_RELEASER_APP_ID }} + private-key: ${{ secrets.GH_APP_POSTHOG_PHP_RELEASER_PRIVATE_KEY }} # Secrets available only inside the 'Release' environment, requires approval from a maintainer + + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + token: ${{ steps.releaser.outputs.token }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Bump version + id: bump-version + run: | + current_version=$(grep -oP "public const VERSION = '\K[^']+" lib/PostHog.php) + IFS='.' read -ra version_parts <<< "$current_version" + major=${version_parts[0]} + minor=${version_parts[1]} + patch=${version_parts[2]} + + if [ "${{ needs.check-release-label.outputs.bump-type }}" == "major" ]; then + new_version="$((major + 1)).0.0" + elif [ "${{ needs.check-release-label.outputs.bump-type }}" == "minor" ]; then + new_version="$major.$((minor + 1)).0" + else + new_version="$major.$minor.$((patch + 1))" + fi + + sed -i "s/public const VERSION = '$current_version'/public const VERSION = '$new_version'/" lib/PostHog.php + sed -i "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" composer.json + + echo "current_version=$current_version" >> $GITHUB_OUTPUT + echo "new_version=$new_version" >> $GITHUB_OUTPUT + + - name: Update CHANGELOG.md + run: | + current_version="${{ steps.bump-version.outputs.current_version }}" + new_version="${{ steps.bump-version.outputs.new_version }}" + release_date=$(date +%Y-%m-%d) + echo -e "## $new_version - $release_date\n\n* [Full Changelog](https://github.com/PostHog/posthog-php/compare/${current_version}...${new_version})\n\n$(cat CHANGELOG.md)" > CHANGELOG.md + + - name: Commit version bump + id: commit-version-bump + run: | + git add lib/PostHog.php composer.json CHANGELOG.md + if git diff --staged --quiet; then + echo "No changes to commit" + echo "committed=false" >> "$GITHUB_OUTPUT" + else + git commit -m "chore: bump version to ${{ steps.bump-version.outputs.new_version }} [version bump]" + git push origin master + echo "committed=true" >> "$GITHUB_OUTPUT" + fi + env: + GITHUB_TOKEN: ${{ steps.releaser.outputs.token }} + + - name: Create and push tag + if: steps.commit-version-bump.outputs.committed == 'true' + run: | + git tag -a "${{ steps.bump-version.outputs.new_version }}" -m "${{ steps.bump-version.outputs.new_version }}" + git push origin "${{ steps.bump-version.outputs.new_version }}" + + - name: Create GitHub release + if: steps.commit-version-bump.outputs.committed == 'true' + env: + GH_TOKEN: ${{ steps.releaser.outputs.token }} + run: | + # Extract the latest changelog entry + LAST_CHANGELOG_ENTRY=$(awk -v defText="see CHANGELOG.md" '/^## /{if (flag) exit; flag=1} flag && /^##$/{exit} flag; END{if (!flag) print defText}' CHANGELOG.md) + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/PostHog/posthog-php/releases \ + -f tag_name="${{ steps.bump-version.outputs.new_version }}" \ + -f target_commitish='master' \ + -f name="${{ steps.bump-version.outputs.new_version }}" \ + -f body="$LAST_CHANGELOG_ENTRY" \ + -F draft=false \ + -F prerelease=false \ + -F generate_release_notes=false + + # Notify in case of a failure + - name: Send failure event to PostHog + if: ${{ failure() }} + uses: PostHog/posthog-github-action@v0.1 + with: + posthog-token: "${{ secrets.POSTHOG_PROJECT_API_KEY }}" + event: "posthog-php-github-release-workflow-failure" + properties: >- + { + "commitSha": "${{ github.sha }}", + "jobStatus": "${{ job.status }}", + "ref": "${{ github.ref }}", + "version": "${{ steps.bump-version.outputs.new_version }}" + } + + - name: Notify Slack - Failed + if: ${{ failure() && needs.notify-approval-needed.outputs.slack_ts != '' }} + uses: posthog/.github/.github/actions/slack-thread-reply@9b04bf3288aca2b4cd3883070858b034b4f7f334 + with: + slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }} + slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }} + thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }} + message: "❌ Failed to release `posthog-php@${{ steps.bump-version.outputs.new_version }}`! " + emoji_reaction: "x" + + notify-released: + name: Notify Slack - Released + needs: [notify-approval-needed, release] + runs-on: ubuntu-latest + if: always() && needs.release.result == 'success' && needs.notify-approval-needed.outputs.slack_ts != '' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Notify Slack - Released + uses: posthog/.github/.github/actions/slack-thread-reply@9b04bf3288aca2b4cd3883070858b034b4f7f334 + with: + slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }} + slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }} + thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }} + message: "🚀 posthog-php released successfully!" + emoji_reaction: "rocket" \ No newline at end of file diff --git a/History.md b/CHANGELOG.md similarity index 100% rename from History.md rename to CHANGELOG.md diff --git a/Makefile b/Makefile index e643909..1b7216a 100644 --- a/Makefile +++ b/Makefile @@ -24,18 +24,6 @@ lint: dependencies printf "Please update PHP version to 5.5 or above for code formatting."; \ fi -release: - @printf "releasing ${VERSION}..." - @if [ "$(shell uname)" = "Darwin" ]; then \ - sed -i '' -E -e "s/public const VERSION = '[^']*'/public const VERSION = '${VERSION}'/" ./lib/PostHog.php; \ - else \ - sed -i -E -e "s/public const VERSION = '[^']*'/public const VERSION = '${VERSION}'/" ./lib/PostHog.php; \ - fi - @node -e "var fs = require('fs'), pkg = require('./composer'); pkg.version = '${VERSION}'; fs.writeFileSync('./composer.json', JSON.stringify(pkg, null, '\t'));" - @git changelog -t ${VERSION} - @git release ${VERSION} - @gh release create ${VERSION} --generate-notes - clean: rm -rf \ composer.phar \ diff --git a/README.md b/README.md index 8804475..395c692 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # PostHog PHP +[![PHP Version](https://img.shields.io/packagist/php-v/posthog/posthog-php?logo=php)](https://packagist.org/packages/posthog/posthog-php) +[![CI](https://github.com/PostHog/posthog-php/actions/workflows/php.yml/badge.svg)](https://github.com/PostHog/posthog-php/actions/workflows/php.yml) + Please see the main [PostHog docs](https://posthog.com/docs). Specifically, the [PHP integration](https://posthog.com/docs/integrations/php-integration) details. @@ -27,3 +30,29 @@ Specifically, the [PHP integration](https://posthog.com/docs/integrations/php-in 1. [Download PHP](https://www.php.net/manual/en/install.php) and [Composer](https://getcomposer.org/download/) 2. `php composer.phar update` to install dependencies 3. `bin/test` to run tests (this script calls `./vendor/bin/phpunit --verbose test`) + +## Releasing + +Releases are semi-automated via GitHub Actions. When a PR with the `release` and a version bump label is merged to `master`, the release workflow is triggered. + +You'll need an approval from a PostHog engineer. If you're an employee, you can see the request in the [#approvals-client-libraries](https://app.slack.com/client/TSS5W8YQZ/C0A3UEVDDNF) channel. + +### Release Process + +1. **Create your PR** with the changes you want to release +2. **Add the `release` label** to the PR +3. **Add a version bump label** that should be either `bump-patch`, `bump-minor` or `bump-major` +4. **Merge the PR** to `master` + +Once merged, the following happens automatically: + +1. A Slack notification is sent to the client libraries channel requesting approval +2. A maintainer approves the release in the GitHub `Release` environment +3. The version is bumped in `lib/PostHog.php` and `composer.json` based on the version label (`patch`, `minor`, or `major`, extracted from the label) +4. The `CHANGELOG.md` is updated with a link to the full changelog +5. Changes are committed and pushed to `master` +6. A git tag is created (e.g., `v1.8.0`) +7. A GitHub release is created with the changelog content +8. Slack is notified of the successful release + +Releases are installed directly from GitHub. diff --git a/RELEASING.md b/RELEASING.md deleted file mode 100644 index 3f4904b..0000000 --- a/RELEASING.md +++ /dev/null @@ -1,14 +0,0 @@ -# Releasing - -1. Make sure you have `make`, `git`, [`git-extras`](https://github.com/tj/git-extras), and [`gh`](https://cli.github.com/) (GitHub CLI) installed. -2. Make sure you're authenticated with GitHub CLI: `gh auth login` -3. Run `VERSION=X.Y.Z make release` (where X.Y.Z is the new version). - -That's it! The release process will: - -- Update the version in `lib/PostHog.php` and `composer.json` -- Create a changelog entry in `History.md` with a list of commits since last release -- Create and push a git tag -- Create a GitHub release with the changelog notes - -Composer will pick up the new tag and you can see the latest version at https://packagist.org/packages/posthog/posthog-php.