From f6764f91aac0c76bced419a3e8297884a1fc1f5c Mon Sep 17 00:00:00 2001 From: Chris Miller Date: Thu, 26 Mar 2026 15:43:04 -0700 Subject: [PATCH 1/3] docs: add release process documentation --- docs/RELEASE_PROCESS.md | 230 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 docs/RELEASE_PROCESS.md diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md new file mode 100644 index 0000000..031e936 --- /dev/null +++ b/docs/RELEASE_PROCESS.md @@ -0,0 +1,230 @@ +# Release Process + +This document covers the complete workflow from creating a feature/bug fix branch through publishing to PyPI. + +## Overview + +The release pipeline uses three main branches: + +- `main` - Primary development branch, receives merged PRs +- `test` - Publishes to Test PyPI for validation +- `release` - Publishes to Production PyPI + +## Stage 1: Create Feature/Bug Fix Branch + +```bash +# Ensure you're on latest main +git checkout main +git pull origin main + +# Create your feature/bug branch +git checkout -b fix/your-bug-name +# or +git checkout -b feat/your-feature-name +``` + +## Stage 2: Develop and Test Locally + +```bash +# Make your changes... + +# Run linting +uv run ruff check src/ tests/ + +# Run type checking +uv run mypy src/ tests/ + +# Run tests +uv run pytest tests/ -v + +# Format code if needed +uv run ruff format src/ tests/ +``` + +## Stage 3: Commit and Push + +```bash +# Stage your changes +git add + +# Commit with descriptive message +git commit -m "fix(component): description of the fix" + +# Push to remote +git push origin fix/your-bug-name +``` + +## Stage 4: Create Pull Request + +1. Go to GitHub and create a PR from your branch to `main` +2. Fill in the PR description with: + - Summary of changes + - Problem being solved + - Testing performed +3. Wait for CI checks to pass (lint, test workflows) +4. Get PR reviewed and approved +5. Merge PR to `main` + +## Stage 5: Publish to Test PyPI (Optional but Recommended) + +After PR is merged to main, publish to Test PyPI for validation: + +```bash +# Fetch latest changes +git fetch origin + +# Switch to test branch +git checkout test +git pull origin test + +# Merge main into test +git merge origin/main -m "Merge main into test for testing" + +# Push to trigger Test PyPI workflow +git push origin test +``` + +The workflow will automatically: + +- Build the package with a dev version (e.g., `1.0.6.dev0`) +- Publish to Test PyPI +- Attempt installation verification + +### Verify Test PyPI Installation + +```bash +pip install --index-url https://test.pypi.org/simple/ \ + --extra-index-url https://pypi.org/simple/ \ + workato-platform-cli + +workato --version +``` + +## Stage 6: Publish to Production PyPI + +**IMPORTANT:** Follow this exact order to avoid workflow failures. + +### Step 1: Determine the new version number + +Check the latest tag: + +```bash +git fetch --tags +git tag --sort=-v:refname | head -5 +``` + +Increment appropriately (e.g., `1.0.5` → `1.0.6`). + +### Step 2: Merge main into release and push + +```bash +# Switch to release branch +git checkout release +git pull origin release + +# Merge main into release +git merge origin/main -m "Merge main into release for v1.0.6" + +# Push release branch +git push origin release +``` + +### Step 3: Create and push the version tag + +**Only after the release branch is pushed:** + +```bash +# Switch to main (or stay on release - they should be at same commit) +git checkout main +git pull origin main + +# Create the version tag +git tag 1.0.6 + +# Push the tag +git push origin 1.0.6 +``` + +### Why This Order Matters + +The PyPI workflow triggers on: + +- Push to `release` branch +- Push of version tags (e.g., `1.0.6`) + +The workflow requires an exact git tag for production releases. If you push the tag first: + +1. Tag push triggers workflow +2. Workflow tries to deploy to `release` environment +3. **FAILS** - Tag is not allowed by environment protection rules (only `release` branch is) + +By pushing the `release` branch first: + +1. Branch push triggers workflow +2. Workflow finds the tag (created locally or already pushed) +3. **SUCCEEDS** - `release` branch is allowed to deploy + +## Workflow Triggers Summary + +| Trigger | Environment | PyPI Target | +| ------------------------ | ----------- | ----------------------------------------------- | +| Push to `test` branch | test | Test PyPI | +| Push to `release` branch | release | Production PyPI | +| Push version tag | release | Production PyPI (but may fail protection rules) | +| Manual workflow_dispatch | Selected | Depends on selection | + +## Troubleshooting + +### "Production releases require an exact git tag" + +The release branch was pushed but no matching tag exists. Create and push the tag: + +```bash +git tag 1.0.X +git push origin 1.0.X +``` + +### "Tag is not allowed to deploy to release due to environment protection rules" + +The tag was pushed before the release branch. This workflow run will fail - ignore it. Push the release branch to trigger a successful run: + +```bash +git checkout release +git merge origin/main +git push origin release +``` + +### Version mismatch or wrong version published + +The version is derived from git tags using `hatch-vcs`. Ensure: + +1. The tag follows semver format: `X.Y.Z` (no `v` prefix) +2. The tag points to the commit being released +3. You've fetched all tags: `git fetch --tags` + +## Quick Reference: Complete Release Commands + +```bash +# After PR is merged to main... + +# 1. Update local main +git checkout main +git pull origin main + +# 2. (Optional) Test on Test PyPI +git checkout test +git pull origin test +git merge origin/main -m "Merge main into test" +git push origin test + +# 3. Release to Production PyPI +git checkout release +git pull origin release +git merge origin/main -m "Merge main into release for vX.Y.Z" +git push origin release + +# 4. Create version tag (after release branch push) +git checkout main +git tag X.Y.Z +git push origin X.Y.Z +``` From df04147c3d29e149071454a96dccad5dc82d5778 Mon Sep 17 00:00:00 2001 From: Chris Miller Date: Thu, 26 Mar 2026 15:44:07 -0700 Subject: [PATCH 2/3] ci: remove tag trigger from PyPI workflow Tags triggered the workflow but failed environment protection rules. The release branch trigger is sufficient - it finds the tag for versioning. --- .github/workflows/pypi.yml | 65 ++++++++++----------- docs/RELEASE_PROCESS.md | 114 +++++++++++++++++++------------------ 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 530e7e5..38dbf71 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -5,13 +5,10 @@ on: branches: - test - release - tags: - - '[0-9]+.[0-9]+.[0-9]+' - - '[0-9]+.[0-9]+.[0-9]+-*' workflow_dispatch: inputs: environment: - description: 'Deployment environment' + description: "Deployment environment" required: true type: choice options: @@ -24,28 +21,28 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} environment: ${{ steps.determine-env.outputs.environment }} - + steps: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 # Required for hatch-vcs versioning - + fetch-depth: 0 # Required for hatch-vcs versioning + - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' - + python-version: "3.11" + - name: Install uv uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - + - name: Install build dependencies run: | uv pip install --system build hatch hatch-vcs twine - + - name: Determine environment id: determine-env run: | @@ -56,7 +53,7 @@ jobs: elif [[ "${{ github.ref }}" == "refs/heads/release" ]] || [[ "${{ github.ref }}" == refs/tags/* ]]; then echo "environment=release" >> $GITHUB_OUTPUT fi - + - name: Get version and configure for environment id: version run: | @@ -73,18 +70,18 @@ jobs: fi echo "Production version: $VERSION" fi - + echo "version=${VERSION}" >> $GITHUB_OUTPUT - + - name: Build package run: python -m build - + - name: Check package run: | twine check dist/* ls -lh dist/ echo "Package version: ${{ steps.version.outputs.version }}" - + - name: Upload artifacts uses: actions/upload-artifact@v4 with: @@ -98,46 +95,46 @@ jobs: environment: name: test url: https://test.pypi.org/project/workato-platform-cli/ - + permissions: - id-token: write # Required for trusted publishing - + id-token: write # Required for trusted publishing + steps: - name: Download artifacts uses: actions/download-artifact@v4 with: name: dist-test path: dist/ - + - name: Publish to Test PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: repository-url: https://test.pypi.org/legacy/ skip-existing: true print-hash: true - + - name: Set up Python for testing uses: actions/setup-python@v5 with: - python-version: '3.11' - + python-version: "3.11" + - name: Install uv uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: "pyproject.toml" - + - name: Test installation run: | echo "Waiting for Test PyPI to process the package..." sleep 30 - + echo "Attempting to install latest workato-platform-cli from TestPyPI" uv pip install --system \ --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ \ workato-platform-cli || echo "Package not yet available on Test PyPI" - + # Verify CLI if installation succeeded workato --version || echo "CLI check skipped" @@ -148,26 +145,26 @@ jobs: environment: name: release url: https://pypi.org/project/workato-platform-cli/ - + permissions: - id-token: write # Required for trusted publishing - contents: write # For creating GitHub releases - + id-token: write # Required for trusted publishing + contents: write # For creating GitHub releases + steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Download artifacts uses: actions/download-artifact@v4 with: name: dist-release path: dist/ - + - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: print-hash: true - + - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v1 @@ -177,4 +174,4 @@ jobs: draft: false prerelease: false env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index 031e936..5af7556 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -92,17 +92,30 @@ The workflow will automatically: ### Verify Test PyPI Installation +Using pipx (recommended): + +```bash +pipx install --python python3.12 \ + --index-url https://test.pypi.org/simple/ \ + --pip-args="--extra-index-url https://pypi.org/simple/" \ + workato-platform-cli==1.0.6.dev0 + +workato --version +``` + +Using pip: + ```bash pip install --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ \ - workato-platform-cli + workato-platform-cli==1.0.6.dev0 workato --version ``` -## Stage 6: Publish to Production PyPI +**Note:** Replace `1.0.6.dev0` with the actual dev version published. Check Test PyPI for available versions: https://test.pypi.org/project/workato-platform-cli/ -**IMPORTANT:** Follow this exact order to avoid workflow failures. +## Stage 6: Publish to Production PyPI ### Step 1: Determine the new version number @@ -115,93 +128,82 @@ git tag --sort=-v:refname | head -5 Increment appropriately (e.g., `1.0.5` → `1.0.6`). -### Step 2: Merge main into release and push +### Step 2: Create the version tag on main -```bash -# Switch to release branch -git checkout release -git pull origin release - -# Merge main into release -git merge origin/main -m "Merge main into release for v1.0.6" - -# Push release branch -git push origin release -``` - -### Step 3: Create and push the version tag - -**Only after the release branch is pushed:** +The version tag must exist before pushing to release, as `hatch-vcs` uses it to determine the package version. ```bash -# Switch to main (or stay on release - they should be at same commit) git checkout main git pull origin main # Create the version tag git tag 1.0.6 -# Push the tag +# Push the tag (this does NOT trigger the PyPI workflow) git push origin 1.0.6 ``` -### Why This Order Matters +### Step 3: Merge main into release and push -The PyPI workflow triggers on: +```bash +# Switch to release branch +git checkout release +git pull origin release -- Push to `release` branch -- Push of version tags (e.g., `1.0.6`) +# Merge main into release +git merge origin/main -m "Merge main into release for v1.0.6" + +# Push release branch (this triggers the PyPI workflow) +git push origin release +``` -The workflow requires an exact git tag for production releases. If you push the tag first: +### How Versioning Works -1. Tag push triggers workflow -2. Workflow tries to deploy to `release` environment -3. **FAILS** - Tag is not allowed by environment protection rules (only `release` branch is) +The PyPI workflow only triggers on branch pushes (`test` or `release`), not on tag pushes. However, tags are still essential: -By pushing the `release` branch first: +- `hatch-vcs` reads git tags to determine the package version +- When the `release` branch is pushed, the workflow runs `git describe --tags --exact-match` to find the version +- If no exact tag exists on the current commit, the build fails with "Production releases require an exact git tag" -1. Branch push triggers workflow -2. Workflow finds the tag (created locally or already pushed) -3. **SUCCEEDS** - `release` branch is allowed to deploy +This is why the tag must be created before (or at the same time as) pushing the release branch. ## Workflow Triggers Summary -| Trigger | Environment | PyPI Target | -| ------------------------ | ----------- | ----------------------------------------------- | -| Push to `test` branch | test | Test PyPI | -| Push to `release` branch | release | Production PyPI | -| Push version tag | release | Production PyPI (but may fail protection rules) | -| Manual workflow_dispatch | Selected | Depends on selection | +| Trigger | Environment | PyPI Target | +| ------------------------ | ----------- | ---------------- | +| Push to `test` branch | test | Test PyPI | +| Push to `release` branch | release | Production PyPI | +| Manual workflow_dispatch | Selected | Depends on input | + +**Note:** Tag pushes do not trigger the workflow. Tags are only used for version detection by `hatch-vcs`. ## Troubleshooting ### "Production releases require an exact git tag" -The release branch was pushed but no matching tag exists. Create and push the tag: +The release branch was pushed but no matching tag exists on the current commit. Create and push the tag: ```bash +git checkout main +git pull origin main git tag 1.0.X git push origin 1.0.X ``` -### "Tag is not allowed to deploy to release due to environment protection rules" - -The tag was pushed before the release branch. This workflow run will fail - ignore it. Push the release branch to trigger a successful run: - -```bash -git checkout release -git merge origin/main -git push origin release -``` +Then push the release branch again (or wait for the tag to be available and re-run the workflow). ### Version mismatch or wrong version published The version is derived from git tags using `hatch-vcs`. Ensure: 1. The tag follows semver format: `X.Y.Z` (no `v` prefix) -2. The tag points to the commit being released +2. The tag points to the exact commit being released 3. You've fetched all tags: `git fetch --tags` +### Test PyPI shows unexpected dev version + +Test PyPI versions are automatically generated as dev versions (e.g., `1.0.6.dev3`). The number increments based on commits since the last tag. This is expected behavior for the test environment. + ## Quick Reference: Complete Release Commands ```bash @@ -217,14 +219,14 @@ git pull origin test git merge origin/main -m "Merge main into test" git push origin test -# 3. Release to Production PyPI +# 3. Create version tag +git checkout main +git tag X.Y.Z +git push origin X.Y.Z + +# 4. Release to Production PyPI git checkout release git pull origin release git merge origin/main -m "Merge main into release for vX.Y.Z" git push origin release - -# 4. Create version tag (after release branch push) -git checkout main -git tag X.Y.Z -git push origin X.Y.Z ``` From 5c5b0b5cb8d076ab8085d315a8aab972320c28bf Mon Sep 17 00:00:00 2001 From: Chris Miller Date: Thu, 26 Mar 2026 17:21:25 -0700 Subject: [PATCH 3/3] Update .github/workflows/pypi.yml Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/pypi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 38dbf71..5e85f29 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -167,7 +167,7 @@ jobs: - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 + if: needs.prepare.outputs.environment == 'release' with: files: dist/* generate_release_notes: true