Skip to content

Commit 2ec3ecc

Browse files
authored
feat(ci/cd): Add releaser integration (#99)
* feat(ci/cd): Add releaser integration * chore: Add the yaml actually * chore: Review feedback * chore: Add release date to the changelog entry * chore: Ensure we do not pass the v prefix when setting a version for consistency * chore: Ensure we do not pass the v prefix when setting a version for consistency
1 parent 04e4df0 commit 2ec3ecc

5 files changed

Lines changed: 246 additions & 26 deletions

File tree

.github/workflows/release.yml

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
name: "Release"
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
branches: [master]
7+
8+
permissions:
9+
contents: read
10+
11+
# Concurrency control: only one release process can run at a time
12+
# This prevents race conditions if multiple PRs with 'release' label merge simultaneously
13+
concurrency:
14+
group: release
15+
cancel-in-progress: false
16+
17+
jobs:
18+
check-release-label:
19+
name: Check for release label
20+
runs-on: ubuntu-latest
21+
# Run when PR with 'release' label is merged to master, or when manually triggered
22+
if: |
23+
github.event.pull_request.merged == true
24+
&& contains(github.event.pull_request.labels.*.name, 'release')
25+
outputs:
26+
should-release: ${{ steps.check.outputs.should-release }}
27+
bump-type: ${{ steps.check.outputs.bump-type }}
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
with:
32+
ref: master
33+
fetch-depth: 0
34+
35+
- name: Check release conditions
36+
id: check
37+
env:
38+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39+
run: |
40+
# Determine bump type from PR labels
41+
if ${{ contains(github.event.pull_request.labels.*.name, 'bump-major') }}; then
42+
echo "bump-type=major" >> "$GITHUB_OUTPUT"
43+
echo "should-release=true" >> "$GITHUB_OUTPUT"
44+
elif ${{ contains(github.event.pull_request.labels.*.name, 'bump-minor') }}; then
45+
echo "bump-type=minor" >> "$GITHUB_OUTPUT"
46+
echo "should-release=true" >> "$GITHUB_OUTPUT"
47+
elif ${{ contains(github.event.pull_request.labels.*.name, 'bump-patch') }}; then
48+
echo "bump-type=patch" >> "$GITHUB_OUTPUT"
49+
echo "should-release=true" >> "$GITHUB_OUTPUT"
50+
fi
51+
52+
notify-approval-needed:
53+
name: Notify Slack - Approval Needed
54+
needs: check-release-label
55+
if: needs.check-release-label.outputs.should-release == 'true'
56+
uses: posthog/.github/.github/workflows/notify-approval-needed.yml@main
57+
with:
58+
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
59+
slack_user_group_id: ${{ vars.GROUP_CLIENT_LIBRARIES_SLACK_GROUP_ID }}
60+
secrets:
61+
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
62+
posthog_project_api_key: ${{ secrets.POSTHOG_PROJECT_API_KEY }}
63+
64+
release:
65+
name: Bump versions and release
66+
needs: [check-release-label, notify-approval-needed]
67+
runs-on: ubuntu-latest
68+
# Use `always()` to ensure the job runs even if the check-release-label job fails
69+
# but still depend on it to be able to use `needs.notify-approval-needed.outputs.slack_ts`
70+
if: always() && needs.check-release-label.outputs.should-release == 'true'
71+
environment: "Release" # This will require an approval from a maintainer, they are notified in Slack above
72+
permissions:
73+
contents: write
74+
actions: write
75+
steps:
76+
- name: Notify Slack - Approved
77+
if: needs.notify-approval-needed.outputs.slack_ts != ''
78+
uses: posthog/.github/.github/actions/slack-thread-reply@main
79+
with:
80+
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
81+
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
82+
thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }}
83+
message: "✅ Release approved! Version bump in progress..."
84+
emoji_reaction: "white_check_mark"
85+
86+
- name: Get GitHub App token
87+
id: releaser
88+
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
89+
with:
90+
app-id: ${{ secrets.GH_APP_POSTHOG_PHP_RELEASER_APP_ID }}
91+
private-key: ${{ secrets.GH_APP_POSTHOG_PHP_RELEASER_PRIVATE_KEY }} # Secrets available only inside the 'Release' environment, requires approval from a maintainer
92+
93+
- name: Checkout repository
94+
uses: actions/checkout@v4
95+
with:
96+
ref: master
97+
fetch-depth: 0
98+
token: ${{ steps.releaser.outputs.token }}
99+
100+
- name: Configure Git
101+
run: |
102+
git config user.name "github-actions[bot]"
103+
git config user.email "github-actions[bot]@users.noreply.github.com"
104+
105+
- name: Bump version
106+
id: bump-version
107+
run: |
108+
current_version=$(grep -oP "public const VERSION = '\K[^']+" lib/PostHog.php)
109+
IFS='.' read -ra version_parts <<< "$current_version"
110+
major=${version_parts[0]}
111+
minor=${version_parts[1]}
112+
patch=${version_parts[2]}
113+
114+
if [ "${{ needs.check-release-label.outputs.bump-type }}" == "major" ]; then
115+
new_version="$((major + 1)).0.0"
116+
elif [ "${{ needs.check-release-label.outputs.bump-type }}" == "minor" ]; then
117+
new_version="$major.$((minor + 1)).0"
118+
else
119+
new_version="$major.$minor.$((patch + 1))"
120+
fi
121+
122+
sed -i "s/public const VERSION = '$current_version'/public const VERSION = '$new_version'/" lib/PostHog.php
123+
sed -i "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" composer.json
124+
125+
echo "current_version=$current_version" >> $GITHUB_OUTPUT
126+
echo "new_version=$new_version" >> $GITHUB_OUTPUT
127+
128+
- name: Update CHANGELOG.md
129+
run: |
130+
current_version="${{ steps.bump-version.outputs.current_version }}"
131+
new_version="${{ steps.bump-version.outputs.new_version }}"
132+
release_date=$(date +%Y-%m-%d)
133+
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
134+
135+
- name: Commit version bump
136+
id: commit-version-bump
137+
run: |
138+
git add lib/PostHog.php composer.json CHANGELOG.md
139+
if git diff --staged --quiet; then
140+
echo "No changes to commit"
141+
echo "committed=false" >> "$GITHUB_OUTPUT"
142+
else
143+
git commit -m "chore: bump version to ${{ steps.bump-version.outputs.new_version }} [version bump]"
144+
git push origin master
145+
echo "committed=true" >> "$GITHUB_OUTPUT"
146+
fi
147+
env:
148+
GITHUB_TOKEN: ${{ steps.releaser.outputs.token }}
149+
150+
- name: Create and push tag
151+
if: steps.commit-version-bump.outputs.committed == 'true'
152+
run: |
153+
git tag -a "${{ steps.bump-version.outputs.new_version }}" -m "${{ steps.bump-version.outputs.new_version }}"
154+
git push origin "${{ steps.bump-version.outputs.new_version }}"
155+
156+
- name: Create GitHub release
157+
if: steps.commit-version-bump.outputs.committed == 'true'
158+
env:
159+
GH_TOKEN: ${{ steps.releaser.outputs.token }}
160+
run: |
161+
# Extract the latest changelog entry
162+
LAST_CHANGELOG_ENTRY=$(awk -v defText="see CHANGELOG.md" '/^## /{if (flag) exit; flag=1} flag && /^##$/{exit} flag; END{if (!flag) print defText}' CHANGELOG.md)
163+
gh api \
164+
--method POST \
165+
-H "Accept: application/vnd.github+json" \
166+
-H "X-GitHub-Api-Version: 2022-11-28" \
167+
/repos/PostHog/posthog-php/releases \
168+
-f tag_name="${{ steps.bump-version.outputs.new_version }}" \
169+
-f target_commitish='master' \
170+
-f name="${{ steps.bump-version.outputs.new_version }}" \
171+
-f body="$LAST_CHANGELOG_ENTRY" \
172+
-F draft=false \
173+
-F prerelease=false \
174+
-F generate_release_notes=false
175+
176+
# Notify in case of a failure
177+
- name: Send failure event to PostHog
178+
if: ${{ failure() }}
179+
uses: PostHog/posthog-github-action@v0.1
180+
with:
181+
posthog-token: "${{ secrets.POSTHOG_PROJECT_API_KEY }}"
182+
event: "posthog-php-github-release-workflow-failure"
183+
properties: >-
184+
{
185+
"commitSha": "${{ github.sha }}",
186+
"jobStatus": "${{ job.status }}",
187+
"ref": "${{ github.ref }}",
188+
"version": "${{ steps.bump-version.outputs.new_version }}"
189+
}
190+
191+
- name: Notify Slack - Failed
192+
if: ${{ failure() && needs.notify-approval-needed.outputs.slack_ts != '' }}
193+
uses: posthog/.github/.github/actions/slack-thread-reply@9b04bf3288aca2b4cd3883070858b034b4f7f334
194+
with:
195+
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
196+
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
197+
thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }}
198+
message: "❌ Failed to release `posthog-php@${{ steps.bump-version.outputs.new_version }}`! <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|View logs>"
199+
emoji_reaction: "x"
200+
201+
notify-released:
202+
name: Notify Slack - Released
203+
needs: [notify-approval-needed, release]
204+
runs-on: ubuntu-latest
205+
if: always() && needs.release.result == 'success' && needs.notify-approval-needed.outputs.slack_ts != ''
206+
steps:
207+
- name: Checkout repository
208+
uses: actions/checkout@v4
209+
210+
- name: Notify Slack - Released
211+
uses: posthog/.github/.github/actions/slack-thread-reply@9b04bf3288aca2b4cd3883070858b034b4f7f334
212+
with:
213+
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
214+
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
215+
thread_ts: ${{ needs.notify-approval-needed.outputs.slack_ts }}
216+
message: "🚀 posthog-php released successfully!"
217+
emoji_reaction: "rocket"
File renamed without changes.

Makefile

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,6 @@ lint: dependencies
2424
printf "Please update PHP version to 5.5 or above for code formatting."; \
2525
fi
2626

27-
release:
28-
@printf "releasing ${VERSION}..."
29-
@if [ "$(shell uname)" = "Darwin" ]; then \
30-
sed -i '' -E -e "s/public const VERSION = '[^']*'/public const VERSION = '${VERSION}'/" ./lib/PostHog.php; \
31-
else \
32-
sed -i -E -e "s/public const VERSION = '[^']*'/public const VERSION = '${VERSION}'/" ./lib/PostHog.php; \
33-
fi
34-
@node -e "var fs = require('fs'), pkg = require('./composer'); pkg.version = '${VERSION}'; fs.writeFileSync('./composer.json', JSON.stringify(pkg, null, '\t'));"
35-
@git changelog -t ${VERSION}
36-
@git release ${VERSION}
37-
@gh release create ${VERSION} --generate-notes
38-
3927
clean:
4028
rm -rf \
4129
composer.phar \

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# PostHog PHP
22

3+
[![PHP Version](https://img.shields.io/packagist/php-v/posthog/posthog-php?logo=php)](https://packagist.org/packages/posthog/posthog-php)
4+
[![CI](https://github.com/PostHog/posthog-php/actions/workflows/php.yml/badge.svg)](https://github.com/PostHog/posthog-php/actions/workflows/php.yml)
5+
36
Please see the main [PostHog docs](https://posthog.com/docs).
47

58
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
2730
1. [Download PHP](https://www.php.net/manual/en/install.php) and [Composer](https://getcomposer.org/download/)
2831
2. `php composer.phar update` to install dependencies
2932
3. `bin/test` to run tests (this script calls `./vendor/bin/phpunit --verbose test`)
33+
34+
## Releasing
35+
36+
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.
37+
38+
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.
39+
40+
### Release Process
41+
42+
1. **Create your PR** with the changes you want to release
43+
2. **Add the `release` label** to the PR
44+
3. **Add a version bump label** that should be either `bump-patch`, `bump-minor` or `bump-major`
45+
4. **Merge the PR** to `master`
46+
47+
Once merged, the following happens automatically:
48+
49+
1. A Slack notification is sent to the client libraries channel requesting approval
50+
2. A maintainer approves the release in the GitHub `Release` environment
51+
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)
52+
4. The `CHANGELOG.md` is updated with a link to the full changelog
53+
5. Changes are committed and pushed to `master`
54+
6. A git tag is created (e.g., `v1.8.0`)
55+
7. A GitHub release is created with the changelog content
56+
8. Slack is notified of the successful release
57+
58+
Releases are installed directly from GitHub.

RELEASING.md

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)