Skip to content

Add early session release for multi-instance workflows #68

Add early session release for multi-instance workflows

Add early session release for multi-instance workflows #68

Workflow file for this run

# RushTI Build & Release Workflow
#
# Triggers:
# - PR to master: Build validation (runs in parallel with Tests)
# - After Tests pass on master push: Full pipeline (build + release + publish)
# - Manual dispatch: On-demand build for any branch
#
# Release flow (master push only, requires PR labels):
# Tests pass → Build exe → Check PR labels → Bump version → Create release → Publish to PyPI
#
# When no release label is present on master push:
# Tests pass → Build exe → Attach build to latest existing release
#
# PR labels: release:major, release:minor, release:patch
name: Build
on:
# PR to master: build validation (parallel with Tests)
pull_request:
branches: [master]
paths:
- 'src/**'
- 'pyproject.toml'
- '*.py'
- '*.spec'
- 'config/**'
- 'assets/**'
- '.github/workflows/build.yml'
# After Tests pass on master push: full pipeline
workflow_run:
workflows: ["Tests"]
types: [completed]
branches: [master]
# On-demand build for any branch
workflow_dispatch:
jobs:
build-windows:
name: Build Windows Executable
runs-on: windows-latest
# Run for: PR, manual dispatch, or workflow_run (only if Tests passed on a push event)
if: >
github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push')
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pyinstaller>=6.0.0
shell: bash
- name: Build executable
run: pyinstaller rushti.spec --clean
shell: bash
- name: Verify build
run: |
echo "Build output structure (onedir mode):"
ls -la dist/
ls -la dist/rushti/
shell: bash
- name: Test executable
run: |
echo "Testing rushti.exe..."
dist/rushti/rushti.exe --help
if [ $? -eq 0 ]; then
echo "Executable runs successfully"
else
echo "Executable failed to run"
exit 1
fi
shell: bash
- name: Create release package
run: |
mkdir -p release/rushti-windows
cp -r dist/rushti/* release/rushti-windows/
mkdir -p release/rushti-windows/config
if [ -f "config/config.ini.template" ]; then
cp config/config.ini.template release/rushti-windows/config/
fi
if [ -f "config/logging_config.ini" ]; then
cp config/logging_config.ini release/rushti-windows/config/
fi
if [ -f "config/settings.ini.template" ]; then
cp config/settings.ini.template release/rushti-windows/config/
fi
if [ -d "assets" ]; then
cp -r assets release/rushti-windows/
fi
cp README.md release/rushti-windows/ || true
cp LICENSE release/rushti-windows/ || true
VERSION=$(python -c "import re, pathlib; print(re.search(r'__version__\s*=\s*\"([^\"]+)\"', pathlib.Path('src/rushti/__init__.py').read_text()).group(1))")
echo "RushTI Windows Build" > release/rushti-windows/BUILD_INFO.txt
echo "Version: $VERSION" >> release/rushti-windows/BUILD_INFO.txt
echo "Build: Complete build with all features (onedir mode for fast startup)" >> release/rushti-windows/BUILD_INFO.txt
echo "Built: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> release/rushti-windows/BUILD_INFO.txt
echo "Python: $(python --version)" >> release/rushti-windows/BUILD_INFO.txt
echo "Commit: ${{ github.sha }}" >> release/rushti-windows/BUILD_INFO.txt
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rushti-windows
path: release/rushti-windows
retention-days: 30
check-release:
name: Check if release needed
needs: build-windows
runs-on: ubuntu-latest
# Only check for releases on master push (not PRs or manual dispatch)
if: >
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push'
outputs:
should_release: ${{ steps.check.outputs.should_release }}
new_version: ${{ steps.check.outputs.new_version }}
bump_type: ${{ steps.check.outputs.bump_type }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get merged PR labels
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMIT_SHA="${{ github.event.workflow_run.head_sha }}"
echo "Checking commit: $COMMIT_SHA"
# Find the PR associated with this commit
PR_DATA=$(gh api repos/${{ github.repository }}/commits/$COMMIT_SHA/pulls --jq '.[0]')
if [ -z "$PR_DATA" ] || [ "$PR_DATA" = "null" ]; then
echo "No PR found for commit, skipping release"
echo "should_release=false" >> $GITHUB_OUTPUT
exit 0
fi
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number')
echo "Found PR #$PR_NUMBER"
# Get PR labels
LABELS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER --jq '.labels[].name')
echo "Labels: $LABELS"
# Determine bump type based on labels
BUMP_TYPE=""
if echo "$LABELS" | grep -q "release:major"; then
BUMP_TYPE="major"
elif echo "$LABELS" | grep -q "release:minor"; then
BUMP_TYPE="minor"
elif echo "$LABELS" | grep -q "release:patch"; then
BUMP_TYPE="patch"
fi
if [ -z "$BUMP_TYPE" ]; then
echo "No release label found, skipping release"
echo "should_release=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Bump type: $BUMP_TYPE"
# Get current version from source
CURRENT_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/rushti/__init__.py)
echo "Current version: $CURRENT_VERSION"
# Parse version components
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
MAJOR=${MAJOR:-0}
MINOR=${MINOR:-0}
PATCH=${PATCH:-0}
# Calculate new version
case $BUMP_TYPE in
major) NEW_VERSION="$((MAJOR + 1)).0.0" ;;
minor) NEW_VERSION="$MAJOR.$((MINOR + 1)).0" ;;
patch) NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" ;;
esac
echo "New version: $NEW_VERSION"
echo "should_release=true" >> $GITHUB_OUTPUT
echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
create-release:
name: Create Release
needs: [build-windows, check-release]
if: needs.check-release.outputs.should_release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Update version in code
run: |
NEW_VERSION="${{ needs.check-release.outputs.new_version }}"
sed -i "s/__version__ = \"[^\"]*\"/__version__ = \"$NEW_VERSION\"/" src/rushti/__init__.py
sed -i "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" pyproject.toml
- name: Commit version bump
run: |
git add src/rushti/__init__.py pyproject.toml
git commit -m "Bump version to ${{ needs.check-release.outputs.new_version }}"
git push
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: rushti-windows
path: artifacts/rushti-windows
- name: Create release archive
run: |
cd artifacts/rushti-windows
zip -r ../rushti-windows.zip .
- name: Create tag and release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ needs.check-release.outputs.new_version }}"
git tag "v$VERSION"
git push origin "v$VERSION"
gh release create "v$VERSION" \
--title "RushTI v$VERSION" \
--generate-notes \
"artifacts/rushti-windows.zip#rushti-windows.zip"
update-release-assets:
name: Update Latest Release Assets
needs: [build-windows, check-release]
# Run when: check-release succeeded but no release label was found
if: >
always() &&
needs.build-windows.result == 'success' &&
needs.check-release.result == 'success' &&
needs.check-release.outputs.should_release != 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: rushti-windows
path: artifacts/rushti-windows
- name: Create release archive
run: |
cd artifacts/rushti-windows
zip -r ../rushti-windows.zip .
- name: Update latest release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
LATEST_TAG=$(gh api repos/${{ github.repository }}/releases/latest --jq '.tag_name' 2>/dev/null)
if [ -z "$LATEST_TAG" ]; then
echo "No existing release found, skipping asset update"
exit 0
fi
echo "Updating assets for release $LATEST_TAG"
gh release upload "$LATEST_TAG" "artifacts/rushti-windows.zip" --clobber
publish-pypi:
name: Publish to PyPI
needs: create-release
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: master
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install build tools
run: python -m pip install --upgrade pip build
- name: Build package
run: python -m build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1