Skip to content

Merge pull request #309 from CJackHwang/codex/update-project-document… #4

Merge pull request #309 from CJackHwang/codex/update-project-document…

Merge pull request #309 from CJackHwang/codex/update-project-document… #4

Workflow file for this run

# Automated Release Workflow for AI Studio Proxy API
# Creates GitHub releases with source code archives when version tags are pushed,
# on pushes to main (nightly builds), or manually triggered via workflow_dispatch.
#
# Release Types:
# - Stable: Push a tag (git tag v0.1.0 && git push origin v0.1.0)
# - Nightly: Automatic on every push to main branch (rolling release)
# - Manual: Actions -> Release -> Run workflow -> Enter version (e.g., v0.2.0)
# The tag will be auto-created on the current HEAD if it doesn't exist.
name: Release
on:
# Trigger on pushes to main branch for nightly builds
push:
branches:
- main
tags:
- 'v*.*.*' # Matches v1.0.0, v2.1.3, etc.
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., v0.1.0 or 0.1.0)'
required: true
type: string
# Ensure only one release workflow runs at a time
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write # Required for creating releases
jobs:
# ===========================================================================
# Stable Release Job
# ===========================================================================
# Runs on tag pushes (v*.*.*) or manual workflow_dispatch
# Creates versioned, stable releases
release:
name: Create Release
runs-on: ubuntu-latest
# Only run on tag pushes or manual triggers (not on main branch pushes)
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
steps:
- name: Validate version format (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: |
VERSION_RAW="${{ github.event.inputs.version }}"
if [[ "$VERSION_RAW" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
VERSION="v$VERSION_RAW"
else
VERSION="$VERSION_RAW"
fi
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "::error::Invalid version format '$VERSION_RAW'. Expected format: v1.2.3, 1.2.3, or pre-release variants like v1.2.3-beta.1"
exit 1
fi
echo "Version format validated: $VERSION"
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for changelog generation
# For workflow_dispatch, checkout default branch first, then verify/checkout tag
# For tag pushes, checkout the tag directly
ref: ${{ github.event_name == 'workflow_dispatch' && '' || github.ref }}
- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION_RAW="${{ github.event.inputs.version }}"
if [[ "$VERSION_RAW" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
VERSION="v$VERSION_RAW"
else
VERSION="$VERSION_RAW"
fi
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT
echo "Release version: $VERSION"
- name: Setup Git for tagging (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Create or verify tag (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: |
VERSION="${{ steps.version.outputs.version }}"
# Fetch all tags to ensure we have the latest
git fetch --tags --force
if git rev-parse "refs/tags/$VERSION" >/dev/null 2>&1; then
echo "✓ Tag $VERSION already exists"
echo "Checking out existing tag..."
git checkout "refs/tags/$VERSION"
else
echo "Tag $VERSION does not exist, creating it on current HEAD..."
CURRENT_SHA=$(git rev-parse HEAD)
echo "Creating tag $VERSION at commit $CURRENT_SHA"
# Create the tag locally
git tag -a "$VERSION" -m "Release $VERSION"
# Push the tag to remote
git push origin "$VERSION"
echo "✓ Tag $VERSION created and pushed successfully"
fi
- name: Generate changelog
id: changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
# Find the previous tag for comparison
PREVIOUS_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | grep -v "^$VERSION$" | head -n 1)
if [ -n "$PREVIOUS_TAG" ]; then
echo "Generating changelog from $PREVIOUS_TAG to $VERSION"
# Generate commit log grouped by type
CHANGELOG=$(cat << 'CHANGELOG_EOF'
## What's Changed
CHANGELOG_EOF
)
TEMP_FILE=$(mktemp)
while IFS= read -r commit_sha || [ -n "$commit_sha" ]; do
if [ -z "$commit_sha" ]; then
continue
fi
commit_msg=$(git log -1 --pretty=format:"%s" "$commit_sha")
short_sha=$(git rev-parse --short "$commit_sha")
git_author=$(git log -1 --pretty=format:"%an" "$commit_sha")
api_response=$(gh api "repos/${{ github.repository }}/commits/$commit_sha" 2>/dev/null || echo '{}')
github_username=$(echo "$api_response" | jq -r '.author.login // empty')
if [ -n "$github_username" ]; then
echo "- ${commit_msg} by @${github_username} (${short_sha})" >> "$TEMP_FILE"
else
echo "- ${commit_msg} by ${git_author} (${short_sha})" >> "$TEMP_FILE"
fi
done < <(git log --pretty=format:"%H" "$PREVIOUS_TAG..$VERSION" 2>/dev/null || true)
if [ -s "$TEMP_FILE" ]; then
CHANGELOG="$CHANGELOG
$(cat "$TEMP_FILE")"
else
CHANGELOG="$CHANGELOG
- Various improvements and updates"
fi
rm -f "$TEMP_FILE"
CHANGELOG="$CHANGELOG
**Full Changelog**: https://github.com/${{ github.repository }}/compare/$PREVIOUS_TAG...$VERSION"
else
echo "No previous tag found, this appears to be the first release"
CHANGELOG="## What's Changed
This is the first automated release in this repository.
See the commit history for details."
fi
# Write to file for use in release
echo "$CHANGELOG" > CHANGELOG.md
echo "Changelog generated successfully"
- name: Build release body
id: release_body
run: |
VERSION="${{ steps.version.outputs.version }}"
VERSION_NUMBER="${{ steps.version.outputs.version_number }}"
cat > RELEASE_BODY.md << 'EOF'
${{ steps.version.outputs.version }} brings improvements and updates to the AI Studio Proxy API.
EOF
# Append the generated changelog
cat CHANGELOG.md >> RELEASE_BODY.md
cat >> RELEASE_BODY.md << 'EOF'
---
## Installation
### Quick Start
```bash
# Clone the repository
git clone https://github.com/${{ github.repository }}.git
cd AIstudioProxyAPI
# Install dependencies with Poetry
poetry install
# Run the server
poetry run python server.py
```
### Docker
```bash
docker-compose -f docker/docker-compose.yml up -d
```
For full installation and configuration details, see the [README](https://github.com/${{ github.repository }}/blob/main/README.md).
## Source Code
Source code archives (zip and tar.gz) are automatically attached below.
---
**Thank you to all contributors!**
EOF
echo "Release body generated successfully"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: "AI Studio Proxy API ${{ steps.version.outputs.version }}"
tag_name: ${{ steps.version.outputs.version }}
body_path: RELEASE_BODY.md
draft: false
prerelease: ${{ contains(steps.version.outputs.version, '-') }}
generate_release_notes: false # We generate our own
make_latest: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release summary
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "## Release Created Successfully!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** $VERSION" >> $GITHUB_STEP_SUMMARY
echo "**Release URL:** https://github.com/${{ github.repository }}/releases/tag/$VERSION" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Included Assets" >> $GITHUB_STEP_SUMMARY
echo "- Source code (zip)" >> $GITHUB_STEP_SUMMARY
echo "- Source code (tar.gz)" >> $GITHUB_STEP_SUMMARY
# ===========================================================================
# Nightly Release Job
# ===========================================================================
# Runs on every push to main branch (not on tags)
# Creates/updates a rolling "nightly" release with the latest development code
nightly:
name: Create Nightly Release
runs-on: ubuntu-latest
# Only run on pushes to main branch, NOT on tag pushes
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for changelog generation
- name: Get build info
id: build_info
run: |
# Get short commit SHA and date for versioning
SHORT_SHA=$(git rev-parse --short HEAD)
BUILD_DATE=$(date +'%Y-%m-%d')
BUILD_TIME=$(date +'%H:%M:%S UTC')
COMMIT_MSG=$(git log -1 --pretty=format:"%s")
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "build_date=$BUILD_DATE" >> $GITHUB_OUTPUT
echo "build_time=$BUILD_TIME" >> $GITHUB_OUTPUT
echo "commit_msg=$COMMIT_MSG" >> $GITHUB_OUTPUT
echo "Build info: $SHORT_SHA @ $BUILD_DATE $BUILD_TIME"
- name: Generate recent changes
id: changes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Build nightly changelog from latest stable tag to HEAD
echo "## What's Changed" > NIGHTLY_CHANGES.md
echo "" >> NIGHTLY_CHANGES.md
LATEST_STABLE_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
if [ -n "$LATEST_STABLE_TAG" ]; then
echo "_Changes since ${LATEST_STABLE_TAG}_" >> NIGHTLY_CHANGES.md
echo "" >> NIGHTLY_CHANGES.md
echo "Using commit range: ${LATEST_STABLE_TAG}..HEAD"
else
echo "_No stable release tag found. Showing last 20 commits (first release scenario)._" >> NIGHTLY_CHANGES.md
echo "" >> NIGHTLY_CHANGES.md
echo "No stable tag found; falling back to last 20 commits"
fi
TEMP_FILE=$(mktemp)
while IFS= read -r commit_sha || [ -n "$commit_sha" ]; do
if [ -z "$commit_sha" ]; then
continue
fi
commit_msg=$(git log -1 --pretty=format:"%s" "$commit_sha")
short_sha=$(git rev-parse --short "$commit_sha")
git_author=$(git log -1 --pretty=format:"%an" "$commit_sha")
api_response=$(gh api "repos/${{ github.repository }}/commits/$commit_sha" 2>/dev/null || echo '{}')
github_username=$(echo "$api_response" | jq -r '.author.login // empty')
if [ -n "$github_username" ]; then
echo "- ${commit_msg} by @${github_username} (${short_sha})" >> "$TEMP_FILE"
else
echo "- ${commit_msg} by ${git_author} (${short_sha})" >> "$TEMP_FILE"
fi
done < <(
if [ -n "$LATEST_STABLE_TAG" ]; then
git log --pretty=format:"%H" "${LATEST_STABLE_TAG}..HEAD" 2>/dev/null || true
else
git log --pretty=format:"%H" -20 2>/dev/null || true
fi
)
if [ -s "$TEMP_FILE" ]; then
cat "$TEMP_FILE" >> NIGHTLY_CHANGES.md
else
if [ -n "$LATEST_STABLE_TAG" ]; then
echo "- No commits found since ${LATEST_STABLE_TAG}" >> NIGHTLY_CHANGES.md
else
echo "- Various improvements and updates" >> NIGHTLY_CHANGES.md
fi
fi
rm -f "$TEMP_FILE"
echo "" >> NIGHTLY_CHANGES.md
echo "Recent changes generated"
- name: Build nightly release body
run: |
cat > NIGHTLY_BODY.md << 'EOF'
## ⚠️ Nightly Build (Development Version)
**This is an automated nightly build from the `main` branch.**
> **Warning**: This release may contain untested features, breaking changes, or bugs.
> For stable releases, please use a [versioned release](https://github.com/${{ github.repository }}/releases?q=v&expanded=true).
### Build Information
- **Commit**: `${{ steps.build_info.outputs.short_sha }}`
- **Date**: ${{ steps.build_info.outputs.build_date }} ${{ steps.build_info.outputs.build_time }}
- **Latest Change**: ${{ steps.build_info.outputs.commit_msg }}
EOF
cat NIGHTLY_CHANGES.md >> NIGHTLY_BODY.md
cat >> NIGHTLY_BODY.md << 'EOF'
---
## Quick Start
```bash
# Clone the repository
git clone https://github.com/${{ github.repository }}.git
cd AIstudioProxyAPI
# Install dependencies
poetry install
# Run the server
poetry run python server.py
```
---
*This nightly release is automatically updated on every push to the main branch.*
**Thank you to all contributors!**
EOF
echo "Nightly release body generated"
- name: Delete existing nightly release
run: |
# Delete existing nightly release if it exists (to avoid accumulation)
echo "Checking for existing nightly release..."
if gh release view nightly &>/dev/null; then
echo "Deleting existing nightly release..."
gh release delete nightly --yes --cleanup-tag
echo "Existing nightly release deleted"
else
echo "No existing nightly release found"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create nightly release
uses: softprops/action-gh-release@v2
with:
name: "Nightly Build (Latest)"
tag_name: nightly
body_path: NIGHTLY_BODY.md
draft: false
prerelease: true # Mark as prerelease since it's development code
generate_release_notes: false
make_latest: false # Don't mark nightly as "latest" - reserve that for stable releases
target_commitish: ${{ github.sha }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Nightly release summary
run: |
echo "## Nightly Release Updated!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** ${{ steps.build_info.outputs.short_sha }}" >> $GITHUB_STEP_SUMMARY
echo "**Build Date:** ${{ steps.build_info.outputs.build_date }}" >> $GITHUB_STEP_SUMMARY
echo "**Release URL:** https://github.com/${{ github.repository }}/releases/tag/nightly" >> $GITHUB_STEP_SUMMARY