|
| 1 | +# GitHub Actions Workflows |
| 2 | + |
| 3 | +This repository uses GitHub Actions for automated testing, releasing, and publishing to PyPI. |
| 4 | + |
| 5 | +## Workflows Overview |
| 6 | + |
| 7 | +### 1. CI Testing (`test.yml`) |
| 8 | + |
| 9 | +**Triggers:** |
| 10 | +- Push to `main` or `develop` branches |
| 11 | +- Pull requests to `main` or `develop` branches |
| 12 | + |
| 13 | +**What it does:** |
| 14 | +- Runs tests across multiple OS (Ubuntu, macOS, Windows) and Python versions (3.8-3.12) |
| 15 | +- Performs linting with black and flake8 |
| 16 | +- Runs type checking with mypy |
| 17 | +- Executes test suite with coverage reporting |
| 18 | +- Uploads coverage to Codecov |
| 19 | +- Builds and validates the package |
| 20 | + |
| 21 | +**Status:** Automatically runs on every push and PR |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +### 2. Create Release (`release.yml`) |
| 26 | + |
| 27 | +**Triggers:** |
| 28 | +- Manual workflow dispatch via GitHub Actions UI |
| 29 | + |
| 30 | +**Required Inputs:** |
| 31 | +- `version`: Version number (e.g., `1.0.0`, `1.2.3-beta.1`) |
| 32 | +- `prerelease`: Boolean flag for pre-release versions (default: `false`) |
| 33 | + |
| 34 | +**What it does:** |
| 35 | +1. Validates version format (semantic versioning) |
| 36 | +2. Checks that the tag doesn't already exist |
| 37 | +3. Runs full test suite |
| 38 | +4. Updates version in `pyproject.toml` and `src/qastudio_pytest/__init__.py` |
| 39 | +5. Builds the package |
| 40 | +6. Commits version bump |
| 41 | +7. Creates and pushes a git tag (`v{version}`) |
| 42 | +8. Generates release notes from git commits |
| 43 | +9. Creates a GitHub Release with built artifacts |
| 44 | + |
| 45 | +**How to use:** |
| 46 | +1. Go to **Actions** → **Create Release** |
| 47 | +2. Click **Run workflow** |
| 48 | +3. Enter the version number (e.g., `1.0.0`) |
| 49 | +4. Select if it's a pre-release |
| 50 | +5. Click **Run workflow** |
| 51 | + |
| 52 | +**Example versions:** |
| 53 | +- Release: `1.0.0`, `2.1.3` |
| 54 | +- Pre-release: `1.0.0-beta.1`, `2.0.0-rc.1` |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +### 3. Publish to PyPI (`publish.yml`) |
| 59 | + |
| 60 | +**Triggers:** |
| 61 | +- Automatically when a GitHub Release is published |
| 62 | +- Manual workflow dispatch via GitHub Actions UI |
| 63 | + |
| 64 | +**Required Secrets:** |
| 65 | +- `PYPI_API_TOKEN`: PyPI API token for publishing to production PyPI |
| 66 | +- `TEST_PYPI_API_TOKEN`: (Optional) TestPyPI API token for testing |
| 67 | + |
| 68 | +**What it does:** |
| 69 | +1. Runs full test suite |
| 70 | +2. Performs linting and type checking |
| 71 | +3. Builds the package |
| 72 | +4. Validates package with twine |
| 73 | +5. Publishes to PyPI or TestPyPI |
| 74 | + |
| 75 | +**Automatic Publishing (on release):** |
| 76 | +- When you create a release using `release.yml`, this workflow automatically triggers |
| 77 | +- Publishes the package to PyPI using the `PYPI_API_TOKEN` secret |
| 78 | + |
| 79 | +**Manual Publishing:** |
| 80 | +1. Go to **Actions** → **Publish to PyPI** |
| 81 | +2. Click **Run workflow** |
| 82 | +3. Select environment: |
| 83 | + - `testpypi`: Publish to TestPyPI (for testing) |
| 84 | + - `pypi`: Publish to production PyPI |
| 85 | +4. Click **Run workflow** |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## Complete Release Process |
| 90 | + |
| 91 | +### Standard Release Flow |
| 92 | + |
| 93 | +1. **Create a Release** |
| 94 | + ``` |
| 95 | + GitHub → Actions → Create Release → Run workflow |
| 96 | + Enter version: 1.0.0 |
| 97 | + ``` |
| 98 | + |
| 99 | +2. **GitHub Actions will:** |
| 100 | + - Run tests |
| 101 | + - Update version files |
| 102 | + - Create git tag |
| 103 | + - Create GitHub Release |
| 104 | + |
| 105 | +3. **Publish to PyPI** (automatic) |
| 106 | + - The `publish.yml` workflow triggers automatically |
| 107 | + - Publishes to PyPI |
| 108 | + |
| 109 | +### Testing Before Production |
| 110 | + |
| 111 | +To test the release process before publishing to production PyPI: |
| 112 | + |
| 113 | +1. **Manually run Publish workflow** |
| 114 | + ``` |
| 115 | + GitHub → Actions → Publish to PyPI → Run workflow |
| 116 | + Environment: testpypi |
| 117 | + ``` |
| 118 | + |
| 119 | +2. **Test installation from TestPyPI** |
| 120 | + ```bash |
| 121 | + pip install --index-url https://test.pypi.org/simple/ qastudio-pytest |
| 122 | + ``` |
| 123 | + |
| 124 | +3. **If everything works, publish to production** |
| 125 | + ``` |
| 126 | + GitHub → Actions → Publish to PyPI → Run workflow |
| 127 | + Environment: pypi |
| 128 | + ``` |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## Required GitHub Secrets |
| 133 | + |
| 134 | +You need to configure these secrets in your GitHub repository: |
| 135 | + |
| 136 | +### `PYPI_API_TOKEN` (Required) |
| 137 | + |
| 138 | +1. Go to [PyPI Account Settings](https://pypi.org/manage/account/) |
| 139 | +2. Scroll to **API tokens** |
| 140 | +3. Click **Add API token** |
| 141 | +4. Name: `github-actions-qastudio-pytest` |
| 142 | +5. Scope: **Project: qastudio-pytest** (or Entire account) |
| 143 | +6. Copy the token (starts with `pypi-`) |
| 144 | +7. Add to GitHub: |
| 145 | + - Go to your repo → **Settings** → **Secrets and variables** → **Actions** |
| 146 | + - Click **New repository secret** |
| 147 | + - Name: `PYPI_API_TOKEN` |
| 148 | + - Value: (paste the token) |
| 149 | + |
| 150 | +### `TEST_PYPI_API_TOKEN` (Optional) |
| 151 | + |
| 152 | +Same process as above, but use [TestPyPI](https://test.pypi.org/manage/account/). |
| 153 | + |
| 154 | +--- |
| 155 | + |
| 156 | +## Workflow Permissions |
| 157 | + |
| 158 | +The workflows require the following permissions: |
| 159 | + |
| 160 | +- **release.yml**: `contents: write` (to create releases and tags) |
| 161 | +- **publish.yml**: `contents: read`, `id-token: write` (for trusted publishing) |
| 162 | + |
| 163 | +These are configured in the workflow files. |
| 164 | + |
| 165 | +--- |
| 166 | + |
| 167 | +## Troubleshooting |
| 168 | + |
| 169 | +### "Tag already exists" error |
| 170 | + |
| 171 | +The tag `v{version}` already exists. Either: |
| 172 | +- Use a different version number |
| 173 | +- Delete the existing tag: `git tag -d v1.0.0 && git push origin :refs/tags/v1.0.0` |
| 174 | + |
| 175 | +### "Invalid version format" error |
| 176 | + |
| 177 | +Version must follow semantic versioning: |
| 178 | +- Valid: `1.0.0`, `2.1.3`, `1.0.0-beta.1` |
| 179 | +- Invalid: `v1.0.0`, `1.0`, `version-1.0.0` |
| 180 | + |
| 181 | +### "Package already exists on PyPI" error |
| 182 | + |
| 183 | +PyPI doesn't allow re-uploading the same version. You need to: |
| 184 | +- Bump the version number |
| 185 | +- Use a new version for the release |
| 186 | + |
| 187 | +### Authentication errors with PyPI |
| 188 | + |
| 189 | +Check that: |
| 190 | +1. `PYPI_API_TOKEN` secret is set correctly |
| 191 | +2. Token hasn't expired |
| 192 | +3. Token has correct scope (project or entire account) |
| 193 | + |
| 194 | +--- |
| 195 | + |
| 196 | +## Version Numbering Guidelines |
| 197 | + |
| 198 | +Follow [Semantic Versioning](https://semver.org/): |
| 199 | + |
| 200 | +- **MAJOR** version (1.0.0 → 2.0.0): Breaking changes |
| 201 | +- **MINOR** version (1.0.0 → 1.1.0): New features, backward compatible |
| 202 | +- **PATCH** version (1.0.0 → 1.0.1): Bug fixes, backward compatible |
| 203 | + |
| 204 | +**Pre-release versions:** |
| 205 | +- Alpha: `1.0.0-alpha.1` |
| 206 | +- Beta: `1.0.0-beta.1` |
| 207 | +- Release Candidate: `1.0.0-rc.1` |
| 208 | + |
| 209 | +--- |
| 210 | + |
| 211 | +## Manual Publishing (without GitHub Actions) |
| 212 | + |
| 213 | +If you need to publish manually: |
| 214 | + |
| 215 | +```bash |
| 216 | +# Install build tools |
| 217 | +pip install --upgrade build twine |
| 218 | + |
| 219 | +# Build the package |
| 220 | +python -m build |
| 221 | + |
| 222 | +# Check the build |
| 223 | +twine check dist/* |
| 224 | + |
| 225 | +# Test on TestPyPI |
| 226 | +twine upload --repository testpypi dist/* |
| 227 | + |
| 228 | +# Publish to PyPI |
| 229 | +twine upload dist/* |
| 230 | +``` |
| 231 | + |
| 232 | +Configure `~/.pypirc`: |
| 233 | +```ini |
| 234 | +[pypi] |
| 235 | + username = __token__ |
| 236 | + password = pypi-YOUR_TOKEN_HERE |
| 237 | + |
| 238 | +[testpypi] |
| 239 | + username = __token__ |
| 240 | + password = pypi-YOUR_TESTPYPI_TOKEN_HERE |
| 241 | +``` |
| 242 | + |
| 243 | +--- |
| 244 | + |
| 245 | +## Monitoring Workflow Runs |
| 246 | + |
| 247 | +- View all workflows: **Actions** tab in GitHub |
| 248 | +- Check individual runs for logs and errors |
| 249 | +- Failed runs will show error messages |
| 250 | +- Download build artifacts from workflow runs if needed |
| 251 | + |
| 252 | +--- |
| 253 | + |
| 254 | +## Best Practices |
| 255 | + |
| 256 | +1. **Always test on TestPyPI first** before publishing to production |
| 257 | +2. **Run tests locally** before creating a release |
| 258 | +3. **Write meaningful release notes** in the GitHub Release |
| 259 | +4. **Follow semantic versioning** strictly |
| 260 | +5. **Tag pre-releases correctly** to avoid confusion |
| 261 | +6. **Keep secrets secure** - never commit them to the repository |
| 262 | +7. **Monitor PyPI downloads** and user feedback after releases |
| 263 | + |
| 264 | +--- |
| 265 | + |
| 266 | +## References |
| 267 | + |
| 268 | +- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/) |
| 269 | +- [GitHub Actions Documentation](https://docs.github.com/en/actions) |
| 270 | +- [Semantic Versioning](https://semver.org/) |
| 271 | +- [PyPI API Tokens](https://pypi.org/help/#apitoken) |
0 commit comments