Add early session release for multi-instance workflows #68
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |