diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 530e7e5..5e85f29 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,33 +145,33 @@ 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 + if: needs.prepare.outputs.environment == 'release' with: files: dist/* generate_release_notes: true 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 new file mode 100644 index 0000000..5af7556 --- /dev/null +++ b/docs/RELEASE_PROCESS.md @@ -0,0 +1,232 @@ +# 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 + +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==1.0.6.dev0 + +workato --version +``` + +**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/ + +## Stage 6: Publish to Production PyPI + +### 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: Create the version tag on main + +The version tag must exist before pushing to release, as `hatch-vcs` uses it to determine the package version. + +```bash +git checkout main +git pull origin main + +# Create the version tag +git tag 1.0.6 + +# Push the tag (this does NOT trigger the PyPI workflow) +git push origin 1.0.6 +``` + +### Step 3: 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 (this triggers the PyPI workflow) +git push origin release +``` + +### How Versioning Works + +The PyPI workflow only triggers on branch pushes (`test` or `release`), not on tag pushes. However, tags are still essential: + +- `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" + +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 | +| 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 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 +``` + +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 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 +# 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. 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 +```