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
72 changes: 72 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Release

# Tag-triggered. Extracts the matching `## <version>` section from
# CHANGELOG.md and creates a GitHub Release. Unlike kyte-api-js, no
# S3 / CloudFront step is needed — kyte-php is composer-distributed,
# and Packagist auto-detects the new tag from GitHub within minutes.
#
# Version comes from the tag (refs/tags/v4.4.0 → 4.4.0). CHANGELOG.md
# must have a `## <version>` H2 anywhere in the file — the script
# extracts everything between that header and the next `## ` as
# release notes.

on:
push:
tags:
- 'v*'

permissions:
contents: write # needed for softprops/action-gh-release

# Opt into Node.js 24 ahead of the 2026-06-02 force-migration.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
release:
name: Create GitHub Release
runs-on: ubuntu-latest

steps:
- name: Check out source
uses: actions/checkout@v5

- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

- name: Verify CHANGELOG.md has matching section
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
if ! grep -q "^## ${VERSION}$" CHANGELOG.md; then
echo "::error::Tag v$VERSION but CHANGELOG.md has no '## $VERSION' section."
echo "::error::Add a CHANGELOG entry before tagging."
exit 1
fi

- name: Extract release notes from CHANGELOG.md
id: notes
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
# Capture everything between `## $VERSION` and the next `## ` line.
awk -v v="^## ${VERSION}$" '
$0 ~ v { capture=1; next }
/^## / && capture { exit }
capture { print }
' CHANGELOG.md > release-notes.md

if [ ! -s release-notes.md ]; then
echo "::error::Could not extract release notes for v$VERSION from CHANGELOG.md"
exit 1
fi

echo "Release notes preview (first 20 lines):"
head -20 release-notes.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: kyte-php v${{ steps.version.outputs.VERSION }}
body_path: release-notes.md
draft: false
prerelease: false
128 changes: 80 additions & 48 deletions release.sh
Original file line number Diff line number Diff line change
@@ -1,50 +1,82 @@
#!/bin/bash
#
# Tag and push a release.
#
# CI (.github/workflows/release.yml) handles the actual GitHub Release
# creation on tag push. This script's only job is to verify CHANGELOG
# matches the version, ensure the working tree is clean, and tag.
#
# kyte-php is distributed via Composer (packagist.org), which auto-
# discovers tags from GitHub. Once the tag exists upstream, customers
# can `composer require keyqcloud/kyte-php:^X.Y` to pull it.
#
# Usage: ./release.sh 4.4.0

print_error() {
echo "\033[1;31m$1\033[0m"
}

if [ "$#" -eq 1 ]; then
# Check the CHANGELOG.md
changelog_version=$(awk '/## /{print $2;exit}' CHANGELOG.md)

if [ "$changelog_version" != "$1" ]; then
print_error "Version in CHANGELOG.md does not match the release version."
exit 1
fi

# Define the path to the Version.php file
php_version_file_path="src/Core/Version.php"

# Extract the version from the PHP file
php_version=$(awk -F '=' '/const MAJOR/{major=$2} /const MINOR/{minor=$2} /const PATCH/{patch=$2} END{print major"."minor"."patch}' "$php_version_file_path")

# Remove semicolons using tr
php_version=$(echo "$php_version" | tr -d ';')

# Compare the desired version with the PHP version
if [ "$1" != "$php_version" ]; then
print_error "Version mismatch: Desired version $1, Version.php is $php_version"
exit 1
fi

echo "Creating tag for release version $1"

git tag "v$1"

if [ $? -eq 0 ]; then
echo "Git tag created successfully for v$1."
# Push the tag to the origin
git push origin --tags

if [ $? -eq 0 ]; then
echo "Git push successful. New release v$1 is available"
else
print_error "Git push failed."
exit 1
fi
else
print_error "Git tag creation failed."
exit 1
fi
fi
set -e

print_error() { printf "\033[1;31m%s\033[0m\n" "$1"; }
print_success() { printf "\033[1;32m%s\033[0m\n" "$1"; }

if [ "$#" -ne 1 ]; then
print_error "Usage: ./release.sh <version>"
print_error "Example: ./release.sh 4.4.0"
exit 1
fi

VERSION="$1"

# Sanity-check CHANGELOG has the new version at the top.
CHANGELOG_VERSION=$(awk '/^## /{print $2; exit}' CHANGELOG.md)
if [ "$CHANGELOG_VERSION" != "$VERSION" ]; then
print_error "Version in CHANGELOG.md ($CHANGELOG_VERSION) does not match $VERSION."
print_error "Update CHANGELOG.md to add a '## $VERSION' section before releasing."
exit 1
fi

# Refuse to tag against a dirty tree — would tag unintended state.
if ! git diff-index --quiet HEAD --; then
print_error "Working tree is not clean. Commit or stash changes first."
git status --short
exit 1
fi

# Refuse to tag if we're not on master.
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" != "master" ]; then
print_error "Releases must be tagged from master. Current branch: $CURRENT_BRANCH"
exit 1
fi

# Refuse to tag if local master is behind origin.
git fetch origin master --quiet
LOCAL=$(git rev-parse master)
REMOTE=$(git rev-parse origin/master)
if [ "$LOCAL" != "$REMOTE" ]; then
print_error "Local master is not in sync with origin/master. Push or pull first."
exit 1
fi

# Refuse to tag if the tag already exists locally or upstream.
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
print_error "Tag v$VERSION already exists locally."
exit 1
fi
if git ls-remote --tags origin "v$VERSION" 2>/dev/null | grep -q "v$VERSION"; then
print_error "Tag v$VERSION already exists on origin."
exit 1
fi

print_success "Tagging v$VERSION..."
git tag "v$VERSION"
git push origin "v$VERSION"

print_success ""
print_success "Tagged v$VERSION and pushed."
print_success "GitHub Actions will now extract release notes from CHANGELOG.md"
print_success "and create the GitHub Release."
print_success "Watch: https://github.com/keyqcloud/kyte-php/actions"
print_success ""
print_success "Once the GitHub Release is published, Packagist will auto-detect"
print_success "the new tag within minutes. Customers pulling via Composer with"
print_success "a constraint matching v$VERSION (e.g., ^${VERSION%%.*}.${VERSION#*.}"
print_success "or ^${VERSION%%.*}.${VERSION#*.}.0) will receive it on next update."
Loading