Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
fetch-depth: 0

- name: Setup .NET Core
uses: actions/setup-dotnet@v3.0.3
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

Expand Down
232 changes: 232 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: Release

on:
pull_request:
types: [closed]
branches: [main]

permissions:
contents: read

concurrency:
group: release
Comment thread
haacked marked this conversation as resolved.
cancel-in-progress: false

jobs:
check-release-label:
name: Check for release label
if: |
github.event.pull_request.merged == true
&& contains(github.event.pull_request.labels.*.name, 'release')
Comment thread
haacked marked this conversation as resolved.
runs-on: ubuntu-latest
outputs:
should-release: ${{ steps.check.outputs.should-release }}
bump-type: ${{ steps.check.outputs.bump-type }}
steps:
- name: Check release conditions
id: check
run: |
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
Comment thread
haacked marked this conversation as resolved.
echo "bump-type=patch" >> "$GITHUB_OUTPUT"
echo "should-release=true" >> "$GITHUB_OUTPUT"
Comment thread
haacked marked this conversation as resolved.
else
echo "should-release=false" >> "$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 version and release
needs: [check-release-label, notify-approval-needed]
runs-on: ubuntu-latest
# Use `always()` so the release proceeds even if the Slack notification fails —
# a Slack outage shouldn't block releases.
if: always() && needs.check-release-label.outputs.should-release == 'true'
environment: Release
permissions:
contents: write
actions: write
Comment thread
haacked marked this conversation as resolved.
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_NOLOGO: true
DOTNET_VERSION: "9.0.102"
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@v2
with:
app-id: ${{ secrets.GH_APP_POSTHOG_DOTNET_RELEASER_APP_ID }}
private-key: ${{ secrets.GH_APP_POSTHOG_DOTNET_RELEASER_PRIVATE_KEY }}

- name: Checkout
uses: actions/checkout@v4
with:
ref: main
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: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
Comment thread
haacked marked this conversation as resolved.

- name: Calculate new version
id: version
run: |
current=$(grep -o '<Version>[^<]*' Directory.Build.props | head -n 1 | sed 's/<Version>//')
IFS='.' read -r major minor patch <<< "$current"
patch=$(echo "$patch" | cut -d- -f1)
case "${{ needs.check-release-label.outputs.bump-type }}" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
esac
Comment thread
haacked marked this conversation as resolved.
new_version="$major.$minor.$patch"
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "new=$new_version" >> "$GITHUB_OUTPUT"
echo "Bumping $current → $new_version"

- name: Update version
run: |
sed -i "s|<Version>${{ steps.version.outputs.current }}</Version>|<Version>${{ steps.version.outputs.new }}</Version>|" Directory.Build.props
Comment thread
haacked marked this conversation as resolved.

- name: Build
run: dotnet build --configuration Release

- name: Test
run: dotnet test --configuration Release --no-build
Comment thread
haacked marked this conversation as resolved.

- name: Commit version bump
id: commit-version-bump
env:
GITHUB_TOKEN: ${{ steps.releaser.outputs.token }}
Comment thread
haacked marked this conversation as resolved.
run: |
git add Directory.Build.props src/PostHog/Generated/VersionConstants.cs
if git diff --staged --quiet; then
echo "No changes to commit"
echo "committed=false" >> "$GITHUB_OUTPUT"
else
git commit -m "Bump version to ${{ steps.version.outputs.new }}"
git push origin main
echo "committed=true" >> "$GITHUB_OUTPUT"
Comment thread
haacked marked this conversation as resolved.
fi

- name: Create tag and GitHub Release
if: steps.commit-version-bump.outputs.committed == 'true'
env:
GH_TOKEN: ${{ steps.releaser.outputs.token }}
run: |
version="${{ steps.version.outputs.new }}"
git tag -a "v$version" -m "v$version"
git push origin "v$version"
gh release create "v$version" --generate-notes

Comment thread
haacked marked this conversation as resolved.
- 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-dotnet-github-release-workflow-failure"
properties: >-
{
"commitSha": "${{ github.sha }}",
"jobStatus": "${{ job.status }}",
"ref": "${{ github.ref }}",
"version": "v${{ steps.version.outputs.new }}"
}
Comment thread
haacked marked this conversation as resolved.

- name: Notify Slack - Failed
if: failure() && 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: "❌ Failed to release `posthog-dotnet@v${{ steps.version.outputs.new }}`! <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|View logs>"
Comment thread
haacked marked this conversation as resolved.
emoji_reaction: "x"

notify-rejected:
name: Notify Slack - Rejected
needs: [release, notify-approval-needed]
runs-on: ubuntu-latest
if: always() && (needs.release.result == 'failure' || needs.release.result == 'skipped') && needs.notify-approval-needed.outputs.slack_ts != ''
steps:
- name: Check for rejection
id: check-rejection
env:
GH_TOKEN: ${{ github.token }}
run: |
RESPONSE=$(gh api /repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/approvals)
REJECTED=$(echo "$RESPONSE" | jq '[.[] | select(.state == "rejected")] | length')
if [ "$REJECTED" -gt 0 ]; then
echo "was_rejected=true" >> "$GITHUB_OUTPUT"
COMMENT=$(echo "$RESPONSE" | jq -r '.[] | select(.state == "rejected") | .comment // empty' | head -1)
if [ -n "$COMMENT" ]; then
{
echo 'message<<EOF'
echo "🚫 Release was rejected: $COMMENT"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
else
echo "message=🚫 Release was rejected." >> "$GITHUB_OUTPUT"
fi
else
echo "was_rejected=false" >> "$GITHUB_OUTPUT"
fi

- name: Notify Slack - Rejected
if: steps.check-rejection.outputs.was_rejected == 'true'
continue-on-error: true
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: '${{ steps.check-rejection.outputs.message }}'
emoji_reaction: 'no_entry_sign'

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: Notify Slack - Released
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: "🚀 posthog-dotnet released successfully!"
emoji_reaction: "rocket"
49 changes: 16 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,47 +73,30 @@ This testing approach ensures broad compatibility without requiring users to ins

## Publishing Releases

To create a release, use the `bin/release` script which automates the version bumping and release preparation process.
Releases are driven by PR labels. When a PR with the right labels is merged to `main`, a GitHub Actions workflow handles version bumping, tagging, creating a GitHub Release (with auto-generated notes), and publishing to NuGet.

### Release Process

1. **Prepare the release**: Run the release script with the type of version bump you want:
1. Add the `release` label and exactly one of `bump-patch`, `bump-minor`, or `bump-major` to your PR
2. Merge the PR to `main`
3. Approve the release in the GitHub Environment gate (the workflow pauses for maintainer approval)
4. The workflow bumps the version in `Directory.Build.props`, commits to `main`, creates a git tag, and creates a GitHub Release
5. The GitHub Release triggers the [`main.yaml`](.github/workflows/main.yaml) workflow, which builds and publishes the packages to NuGet

```bash
# For a patch release (1.0.6 -> 1.0.7)
./bin/release patch

# For a minor release (1.0.6 -> 1.1.0)
./bin/release minor

# For a major release (1.0.6 -> 2.0.0)
./bin/release major
```
### Manual Fallback

This script will:
- Calculate the new version based on the current version in [`Directory.Build.props`](Directory.Build.props)
- Create a new release branch (`release-{version}`)
- Update the version in `Directory.Build.props`
- Build and test the solution
- Commit the version change and generated files
- Create and push the git tag
If you need to perform an emergency release bypassing the automated workflow, the `bin/release-local` script is available:

2. **Create a Pull Request**: Push the release branch and create a PR to merge it into `main`:

```bash
git push origin release-{version}
```

Then create a pull request on GitHub to merge the release branch into `main`.

3. **Create GitHub Release**: Once the PR is merged, go to GitHub to [Draft a new Release](https://github.com/Posthog/posthog-dotnet/releases/new), select the tag that was already created, and click "Auto-generate release notes". Edit the notes as needed and publish the release.
```bash
# For a patch release (1.0.6 -> 1.0.7)
./bin/release-local patch

When you create the Release, the [`main.yml`](.github/workflows/main.yml) workflow builds and publishes the package to NuGet.
# For a minor release (1.0.6 -> 1.1.0)
./bin/release-local minor

> [!IMPORTANT]
> When creating a release, it's important to create and publish it in one go. If you save a draft of the release and
> then later publish it, the workflow will not run. If you find yourself in that position, you can [manually trigger the workflow run](https://github.com/PostHog/posthog-dotnet/actions/workflows/main.yaml)
> and select the tag to publish.
# For a major release (1.0.6 -> 2.0.0)
./bin/release-local major
```

## Installation

Expand Down
File renamed without changes.
Loading