Skip to content
Open
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/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true

jobs:
Expand Down
54 changes: 0 additions & 54 deletions .github/workflows/initiate_release.yml

This file was deleted.

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

on:
workflow_dispatch:
inputs:
version:
description: "Release version (example: 4.2.0)"
required: true
type: string

permissions:
contents: write

jobs:
manual-release:
name: 🚀 Manual Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23"

- name: Update version file
env:
VERSION: ${{ github.event.inputs.version }}
run: |
sed -i "s|versionName = \".*\"|versionName = \"v${VERSION}\"|" version.go

- name: Commit version update
env:
VERSION: ${{ github.event.inputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version.go
if git diff --cached --quiet; then
echo "No version changes to commit."
else
git commit -m "chore(release): v${VERSION} (manual)"
git push origin HEAD:main
fi

- name: Run tests
env:
STREAM_BASE_URL: ${{ vars.STREAM_BASE_URL }}
STREAM_API_KEY: ${{ vars.STREAM_API_KEY }}
STREAM_API_SECRET: ${{ secrets.STREAM_API_SECRET }}
run: go test -short -v ./...

- name: Create and push tag
env:
VERSION: ${{ github.event.inputs.version }}
run: |
git tag "v${VERSION}"
git push origin "v${VERSION}"

- name: Create release on GitHub
uses: ncipollo/release-action@v1
with:
tag: v${{ github.event.inputs.version }}
token: ${{ secrets.GITHUB_TOKEN }}
generateReleaseNotes: true
82 changes: 71 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,85 @@ on:
branches:
- main

concurrency:
group: release-${{ github.event.pull_request.base.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
Release:
release:
name: 🚀 Release
if: github.event.pull_request.merged && startsWith(github.head_ref, 'release-')
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/github-script@v6
with:
script: |
// Getting the release version from the PR source branch
// Source branch looks like this: release-1.0.0
const version = context.payload.pull_request.head.ref.split('-')[1]
core.exportVariable('VERSION', version)
fetch-depth: 0
ref: ${{ github.event.pull_request.base.ref }}

- name: Skip when PR is already released
id: already_released
run: |
if git log --oneline --grep="(pr #${{ github.event.pull_request.number }})" -n 1 | grep -q "chore(release):"; then
echo "value=true" >> "$GITHUB_OUTPUT"
else
echo "value=false" >> "$GITHUB_OUTPUT"
fi

- name: Determine and apply version bump
id: release_meta
if: steps.already_released.outputs.value != 'true'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
PR_BODY_FILE=$(mktemp)
printf '%s' "$PR_BODY" > "$PR_BODY_FILE"
bash scripts/release/bump_version.sh \
--title "$PR_TITLE" \
--body-file "$PR_BODY_FILE" \
--output "$GITHUB_OUTPUT"

- name: Stop when PR does not require release
if: steps.already_released.outputs.value == 'true' || steps.release_meta.outputs.should_release != 'true'
run: |
if [ "${{ steps.already_released.outputs.value }}" = "true" ]; then
echo "PR #${{ github.event.pull_request.number }} is already released; skipping."
exit 0
fi
echo "No release type found in PR title; skipping."
exit 0

- name: Commit version files
if: steps.already_released.outputs.value != 'true' && steps.release_meta.outputs.should_release == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version.go
if git diff --cached --quiet; then
echo "No version changes to commit."
exit 0
fi
git commit -m "chore(release): v${{ steps.release_meta.outputs.version }} (pr #${{ github.event.pull_request.number }})"
git push origin "HEAD:${{ github.event.pull_request.base.ref }}"

- name: Create release tag
if: steps.already_released.outputs.value != 'true' && steps.release_meta.outputs.should_release == 'true'
run: |
git tag "${{ steps.release_meta.outputs.tag }}"
git push origin "${{ steps.release_meta.outputs.tag }}"

- name: Create release on GitHub
if: steps.already_released.outputs.value != 'true' && steps.release_meta.outputs.should_release == 'true'
uses: ncipollo/release-action@v1
with:
tag: ${{ env.VERSION }}
tag: ${{ steps.release_meta.outputs.tag }}
token: ${{ secrets.GITHUB_TOKEN }}
generateReleaseNotes: true
body: |
Release v${{ steps.release_meta.outputs.version }}

- Bump type: `${{ steps.release_meta.outputs.bump }}`
- Previous: `${{ steps.release_meta.outputs.previous_version }}`
- Next: `${{ steps.release_meta.outputs.version }}`
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,18 @@ We've recently closed a [$38 million Series B funding round](https://techcrunch.
Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world.

Check out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs).

## Release Process

Releases use two paths:

- Default: automatic release when a PR is merged to `main`.
- Fallback: manual release using `.github/workflows/manual-release.yml` (admin use only).

Automatic semver bump rules are based on merged PR title/body:

- `feat:` -> minor
- `fix:` (or `bug:`) -> patch
- `feat!:` or `BREAKING CHANGE` in PR body -> major

PRs with other prefixes do not trigger a release.
114 changes: 114 additions & 0 deletions scripts/release/bump_version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env bash

set -euo pipefail

title=""
body=""
body_file=""
output=""

while [[ $# -gt 0 ]]; do
case "$1" in
--title)
title="${2:-}"
shift 2
;;
--body)
body="${2:-}"
shift 2
;;
--body-file)
body_file="${2:-}"
shift 2
;;
--output)
output="${2:-}"
shift 2
;;
*)
echo "unknown argument: $1" >&2
exit 1
;;
esac
done

if [[ -n "${body_file}" ]]; then
body="$(<"${body_file}")"
fi

determine_bump() {
local pr_title="$1"
local pr_body="$2"

if [[ "${pr_body}" =~ BREAKING[[:space:]-]CHANGE ]]; then
echo "major"
return
fi

if ! echo "${pr_title}" | grep -Eq '^([a-zA-Z]+)(\([^)]+\))?(!)?:'; then
echo "none"
return
fi

if echo "${pr_title}" | grep -Eq '^([a-zA-Z]+)(\([^)]+\))?!:'; then
echo "major"
return
fi

local type
type="$(echo "${pr_title}" | sed -E 's/^([a-zA-Z]+).*/\1/' | tr '[:upper:]' '[:lower:]')"
if [[ "${type}" == "feat" ]]; then
echo "minor"
return
fi
if [[ "${type}" == "fix" || "${type}" == "bug" ]]; then
echo "patch"
return
fi

echo "none"
}

bump="$(determine_bump "${title}" "${body}")"

write_output() {
local key="$1"
local value="$2"
if [[ -n "${output}" ]]; then
echo "${key}=${value}" >> "${output}"
else
echo "${key}=${value}"
fi
}

if [[ "${bump}" == "none" ]]; then
write_output "should_release" "false"
write_output "bump" "none"
exit 0
fi

latest_version="$(git tag --list | sed 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1)"
if [[ -z "${latest_version}" ]]; then
latest_version="0.0.0"
fi

IFS='.' read -r major minor patch <<< "${latest_version}"
case "${bump}" in
major)
next_version="$((major + 1)).0.0"
;;
minor)
next_version="${major}.$((minor + 1)).0"
;;
patch)
next_version="${major}.${minor}.$((patch + 1))"
;;
esac

sed -i "s|versionName = \".*\"|versionName = \"v${next_version}\"|" version.go

write_output "should_release" "true"
write_output "bump" "${bump}"
write_output "previous_version" "${latest_version}"
write_output "version" "${next_version}"
write_output "tag" "v${next_version}"
Loading