treemapper CD #27
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
| # .github/workflows/cd.yml | |
| name: TreeMapper CD | |
| permissions: | |
| contents: write | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to release (e.g., 1.0.0)' | |
| required: true | |
| publish_to_pypi: | |
| description: 'Publish to PyPI' | |
| required: true | |
| default: 'false' | |
| type: choice | |
| options: | |
| - 'true' | |
| - 'false' | |
| jobs: | |
| prepare-version: | |
| name: Prepare Version Commit | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.set_outputs.outputs.version }} | |
| tag_name: ${{ steps.set_outputs.outputs.tag_name }} | |
| commit_sha: ${{ steps.commit_version.outputs.commit_sha }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v5 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| - name: Check that we're on main branch | |
| run: | | |
| CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) | |
| if [ "$CURRENT_BRANCH" != "main" ]; then | |
| echo "Error: Releases can only be created from the main branch. Current branch: $CURRENT_BRANCH" | |
| exit 1 | |
| fi | |
| - name: Set version in version.py | |
| env: | |
| VERSION: ${{ github.event.inputs.version }} | |
| run: | | |
| echo "Setting version to $VERSION" | |
| python - <<'PY' | |
| import os, re, pathlib | |
| ver = os.environ["VERSION"] | |
| p = pathlib.Path("src/treemapper/version.py") | |
| s = p.read_text(encoding="utf-8") | |
| s, n = re.subn(r'__version__\s*=\s*["\'].*?["\']', f'__version__ = "{ver}"', s, count=1) | |
| assert n == 1, "version assignment not found or multiple matches" | |
| p.write_text(s, encoding="utf-8") | |
| PY | |
| echo "version.py content after change:" | |
| cat src/treemapper/version.py | |
| - name: Commit version bump (locally only, no push yet) | |
| id: commit_version | |
| run: | | |
| git config user.name github-actions[bot] | |
| git config user.email 41898282+github-actions[bot]@users.noreply.github.com | |
| git add src/treemapper/version.py | |
| if ! git diff --staged --quiet; then | |
| git commit -m "Release version ${{ github.event.inputs.version }}" | |
| else | |
| echo "No changes to commit." | |
| fi | |
| COMMIT_SHA=$(git rev-parse HEAD) | |
| echo "Commit SHA: $COMMIT_SHA" | |
| echo "commit_sha=$COMMIT_SHA" >> $GITHUB_OUTPUT | |
| - name: Create local tag (no push yet) | |
| run: | | |
| git tag -a "v${{ github.event.inputs.version }}" -m "Release version ${{ github.event.inputs.version }}" | |
| echo "Tag created locally: v${{ github.event.inputs.version }}" | |
| - name: Upload git bundle for other jobs | |
| run: | | |
| git bundle create repo.bundle --all | |
| - name: Upload bundle as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: git-repo-bundle | |
| path: repo.bundle | |
| retention-days: 1 | |
| - name: Set outputs | |
| id: set_outputs | |
| run: | | |
| echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT | |
| echo "tag_name=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT | |
| build-assets: | |
| name: Build Assets | |
| needs: prepare-version | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| asset_name: linux | |
| python-version: '3.11' | |
| - os: macos-latest | |
| asset_name: macos | |
| python-version: '3.11' | |
| - os: windows-latest | |
| asset_name: windows | |
| python-version: '3.11' | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Download git bundle | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: git-repo-bundle | |
| - name: Restore repository from bundle | |
| run: | | |
| git clone repo.bundle repo | |
| cd repo | |
| git checkout ${{ needs.prepare-version.outputs.tag_name }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Cache pip Dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-${{ matrix.python-version }}- | |
| ${{ runner.os }}-pip- | |
| - name: Install Dependencies (including PyInstaller) | |
| working-directory: ./repo | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install .[dev] | |
| - name: Build with PyInstaller | |
| working-directory: ./repo | |
| run: | | |
| python -m PyInstaller --clean -y --dist ./dist/${{ matrix.asset_name }} treemapper.spec | |
| - name: Determine architecture | |
| id: arch | |
| shell: bash | |
| run: | | |
| ARCH=$(uname -m) | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| if [[ "${{ runner.arch }}" == "X64" ]]; then ARCH="x86_64"; \ | |
| elif [[ "${{ runner.arch }}" == "ARM64" ]]; then ARCH="arm64"; \ | |
| else ARCH="unknown"; fi | |
| elif [[ "${{ runner.os }}" == "macOS" ]] && [[ "$ARCH" == "arm64" ]]; then | |
| echo "Detected ARM on macOS" | |
| fi | |
| echo "Determined ARCH: $ARCH" | |
| echo "arch=$ARCH" >> $GITHUB_OUTPUT | |
| - name: Rename asset with proper name | |
| shell: bash | |
| working-directory: ./repo | |
| run: | | |
| ASSET_NAME="treemapper-${{ matrix.asset_name }}-${{ steps.arch.outputs.arch }}" | |
| ASSET_NAME="${ASSET_NAME}-v${{ needs.prepare-version.outputs.version }}" | |
| if [[ "${{ runner.os }}" == "Windows" ]]; then | |
| ASSET_NAME="${ASSET_NAME}.exe" | |
| mv "./dist/${{ matrix.asset_name }}/treemapper.exe" "./dist/${ASSET_NAME}" | |
| else | |
| mv "./dist/${{ matrix.asset_name }}/treemapper" "./dist/${ASSET_NAME}" | |
| fi | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.asset_name }}-binary | |
| path: ./repo/dist/treemapper-* | |
| retention-days: 1 | |
| publish-to-pypi: | |
| name: Publish to PyPI | |
| needs: [prepare-version, build-assets] | |
| if: github.event.inputs.publish_to_pypi == 'true' | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/treemapper | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download git bundle | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: git-repo-bundle | |
| - name: Restore repository from bundle | |
| run: | | |
| git clone repo.bundle repo | |
| cd repo | |
| git checkout ${{ needs.prepare-version.outputs.tag_name }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.11' | |
| - name: Install build tools | |
| working-directory: ./repo | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build | |
| - name: Build sdist and wheel | |
| working-directory: ./repo | |
| run: python -m build | |
| - name: Publish package distributions to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: ./repo/dist/ | |
| finalize-release: | |
| name: Push Release and Create GitHub Release | |
| needs: [prepare-version, build-assets, publish-to-pypi] | |
| if: | | |
| always() && | |
| needs.prepare-version.result == 'success' && | |
| needs.build-assets.result == 'success' && | |
| (needs.publish-to-pypi.result == 'success' || | |
| needs.publish-to-pypi.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download git bundle | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: git-repo-bundle | |
| - name: Restore repository from bundle | |
| run: | | |
| git clone repo.bundle repo | |
| cd repo | |
| - name: Push commit and tag to main | |
| working-directory: ./repo | |
| run: | | |
| git config user.name github-actions[bot] | |
| git config user.email 41898282+github-actions[bot]@users.noreply.github.com | |
| # Push the version bump commit to main | |
| git push origin HEAD:main | |
| # Push the tag | |
| git push origin ${{ needs.prepare-version.outputs.tag_name }} | |
| echo "Successfully pushed version commit and tag" | |
| - name: Download all build artifacts | |
| uses: actions/download-artifact@v5 | |
| with: | |
| path: ./artifacts | |
| - name: Create GitHub Release with assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.prepare-version.outputs.tag_name }} | |
| name: Release ${{ needs.prepare-version.outputs.version }} | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| files: ./artifacts/**/*-binary/treemapper-* |