|
| 1 | +# PyPI Publishing Setup Guide |
| 2 | + |
| 3 | +This guide explains how to set up automated publishing to PyPI when you push a version tag. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +1. **PyPI Account** - Create an account at https://pypi.org |
| 8 | +2. **Test PyPI Account** (optional but recommended) - Create an account at https://test.pypi.org |
| 9 | +3. **GitHub Repository Access** - Admin access to configure secrets |
| 10 | + |
| 11 | +## Step 1: Generate PyPI API Tokens |
| 12 | + |
| 13 | +### For Production PyPI |
| 14 | + |
| 15 | +1. Log in to https://pypi.org |
| 16 | +2. Go to Account Settings → API tokens |
| 17 | +3. Click "Add API token" |
| 18 | +4. Set the token name: `django-forms-workflows-github-actions` |
| 19 | +5. Set the scope: |
| 20 | + - **Option A (Recommended):** Scope to project `django-forms-workflows` (after first manual upload) |
| 21 | + - **Option B:** Scope to entire account (less secure) |
| 22 | +6. Click "Add token" |
| 23 | +7. **IMPORTANT:** Copy the token immediately (starts with `pypi-`) |
| 24 | +8. Store it securely - you won't be able to see it again |
| 25 | + |
| 26 | +### For Test PyPI (Optional) |
| 27 | + |
| 28 | +1. Log in to https://test.pypi.org |
| 29 | +2. Follow the same steps as above |
| 30 | +3. Token name: `django-forms-workflows-github-actions-test` |
| 31 | +4. Copy and store the token |
| 32 | + |
| 33 | +## Step 2: Add Secrets to GitHub Repository |
| 34 | + |
| 35 | +1. Go to your GitHub repository |
| 36 | +2. Navigate to **Settings** → **Secrets and variables** → **Actions** |
| 37 | +3. Click **New repository secret** |
| 38 | +4. Add the following secrets: |
| 39 | + |
| 40 | +### Required Secret |
| 41 | + |
| 42 | +**Name:** `PYPI_API_TOKEN` |
| 43 | +**Value:** The PyPI API token you generated (starts with `pypi-`) |
| 44 | + |
| 45 | +### Optional Secret (for testing) |
| 46 | + |
| 47 | +**Name:** `TEST_PYPI_API_TOKEN` |
| 48 | +**Value:** The Test PyPI API token you generated |
| 49 | + |
| 50 | +## Step 3: First Manual Upload (Recommended) |
| 51 | + |
| 52 | +For the first release, it's recommended to manually upload to PyPI to register the project: |
| 53 | + |
| 54 | +```bash |
| 55 | +# Install build tools |
| 56 | +pip install build twine |
| 57 | + |
| 58 | +# Build the package |
| 59 | +python -m build |
| 60 | + |
| 61 | +# Upload to Test PyPI (optional - for testing) |
| 62 | +twine upload --repository testpypi dist/* |
| 63 | + |
| 64 | +# Upload to PyPI |
| 65 | +twine upload dist/* |
| 66 | +``` |
| 67 | + |
| 68 | +You'll be prompted for your PyPI username and password/token: |
| 69 | +- Username: `__token__` |
| 70 | +- Password: Your API token (including the `pypi-` prefix) |
| 71 | + |
| 72 | +## Step 4: Publishing a New Version |
| 73 | + |
| 74 | +Once the GitHub Actions workflow is set up, publishing is automatic: |
| 75 | + |
| 76 | +### 1. Update Version Numbers |
| 77 | + |
| 78 | +Update the version in these files: |
| 79 | +- `django_forms_workflows/__init__.py` |
| 80 | +- `setup.py` |
| 81 | +- `pyproject.toml` |
| 82 | +- `CHANGELOG.md` |
| 83 | + |
| 84 | +Example for version 0.2.0: |
| 85 | + |
| 86 | +```python |
| 87 | +# django_forms_workflows/__init__.py |
| 88 | +__version__ = '0.2.0' |
| 89 | +``` |
| 90 | + |
| 91 | +```python |
| 92 | +# setup.py |
| 93 | +setup( |
| 94 | + name='django-forms-workflows', |
| 95 | + version='0.2.0', |
| 96 | + ... |
| 97 | +) |
| 98 | +``` |
| 99 | + |
| 100 | +```toml |
| 101 | +# pyproject.toml |
| 102 | +[tool.poetry] |
| 103 | +name = "django-forms-workflows" |
| 104 | +version = "0.2.0" |
| 105 | +``` |
| 106 | + |
| 107 | +### 2. Update CHANGELOG.md |
| 108 | + |
| 109 | +Document all changes in `CHANGELOG.md`: |
| 110 | + |
| 111 | +```markdown |
| 112 | +## [0.2.0] - 2025-11-01 |
| 113 | + |
| 114 | +### Added |
| 115 | +- New feature X |
| 116 | +- New feature Y |
| 117 | + |
| 118 | +### Changed |
| 119 | +- Updated feature Z |
| 120 | + |
| 121 | +### Fixed |
| 122 | +- Bug fix A |
| 123 | +``` |
| 124 | + |
| 125 | +### 3. Commit and Push Changes |
| 126 | + |
| 127 | +```bash |
| 128 | +git add . |
| 129 | +git commit -m "Bump version to 0.2.0" |
| 130 | +git push origin main |
| 131 | +``` |
| 132 | + |
| 133 | +### 4. Create and Push a Tag |
| 134 | + |
| 135 | +```bash |
| 136 | +# Create an annotated tag |
| 137 | +git tag -a v0.2.0 -m "Release version 0.2.0" |
| 138 | + |
| 139 | +# Push the tag to GitHub |
| 140 | +git push origin v0.2.0 |
| 141 | +``` |
| 142 | + |
| 143 | +### 5. Automated Publishing |
| 144 | + |
| 145 | +The GitHub Actions workflow will automatically: |
| 146 | +1. ✅ Verify the tag version matches the package version |
| 147 | +2. ✅ Build the distribution packages (wheel and source) |
| 148 | +3. ✅ Check the packages with twine |
| 149 | +4. ✅ Publish to PyPI (for stable releases) |
| 150 | +5. ✅ Create a GitHub Release with the distribution files |
| 151 | + |
| 152 | +## Release Types |
| 153 | + |
| 154 | +### Stable Release |
| 155 | + |
| 156 | +Tag format: `v0.2.0`, `v1.0.0`, etc. |
| 157 | + |
| 158 | +```bash |
| 159 | +git tag -a v0.2.0 -m "Release version 0.2.0" |
| 160 | +git push origin v0.2.0 |
| 161 | +``` |
| 162 | + |
| 163 | +This will publish to **PyPI** and create a **GitHub Release**. |
| 164 | + |
| 165 | +### Release Candidate (Test) |
| 166 | + |
| 167 | +Tag format: `v0.2.0-rc1`, `v1.0.0-rc2`, etc. |
| 168 | + |
| 169 | +```bash |
| 170 | +git tag -a v0.2.0-rc1 -m "Release candidate 0.2.0-rc1" |
| 171 | +git push origin v0.2.0-rc1 |
| 172 | +``` |
| 173 | + |
| 174 | +This will publish to **Test PyPI** only (if configured). |
| 175 | + |
| 176 | +## Monitoring the Workflow |
| 177 | + |
| 178 | +1. Go to your GitHub repository |
| 179 | +2. Click on the **Actions** tab |
| 180 | +3. Find the "Publish to PyPI" workflow run |
| 181 | +4. Monitor the progress and check for any errors |
| 182 | + |
| 183 | +## Troubleshooting |
| 184 | + |
| 185 | +### Error: Version mismatch |
| 186 | + |
| 187 | +**Problem:** Tag version doesn't match package version |
| 188 | + |
| 189 | +**Solution:** Ensure all version numbers are updated: |
| 190 | +- `django_forms_workflows/__init__.py` |
| 191 | +- `setup.py` |
| 192 | +- `pyproject.toml` |
| 193 | + |
| 194 | +### Error: Package already exists |
| 195 | + |
| 196 | +**Problem:** Version already published to PyPI |
| 197 | + |
| 198 | +**Solution:** |
| 199 | +- You cannot overwrite a published version |
| 200 | +- Increment the version number and create a new tag |
| 201 | +- Consider using patch versions (e.g., 0.2.1) |
| 202 | + |
| 203 | +### Error: Invalid credentials |
| 204 | + |
| 205 | +**Problem:** PyPI API token is incorrect or expired |
| 206 | + |
| 207 | +**Solution:** |
| 208 | +1. Generate a new API token on PyPI |
| 209 | +2. Update the `PYPI_API_TOKEN` secret in GitHub |
| 210 | +3. Re-run the workflow |
| 211 | + |
| 212 | +### Error: Package name already taken |
| 213 | + |
| 214 | +**Problem:** Another package with the same name exists |
| 215 | + |
| 216 | +**Solution:** |
| 217 | +- Choose a different package name |
| 218 | +- Update `name` in `setup.py` and `pyproject.toml` |
| 219 | +- Update all references in documentation |
| 220 | + |
| 221 | +## Best Practices |
| 222 | + |
| 223 | +1. **Test First** - Always test with Test PyPI before publishing to production |
| 224 | +2. **Semantic Versioning** - Follow semver (MAJOR.MINOR.PATCH) |
| 225 | +3. **Changelog** - Always update CHANGELOG.md before releasing |
| 226 | +4. **Git Tags** - Use annotated tags with descriptive messages |
| 227 | +5. **Version Consistency** - Ensure all version numbers match |
| 228 | +6. **Review Changes** - Review the diff before tagging |
| 229 | +7. **CI Passing** - Ensure all CI checks pass before releasing |
| 230 | + |
| 231 | +## Version Numbering Guide |
| 232 | + |
| 233 | +Follow [Semantic Versioning](https://semver.org/): |
| 234 | + |
| 235 | +- **MAJOR** (1.0.0) - Breaking changes |
| 236 | +- **MINOR** (0.2.0) - New features, backward compatible |
| 237 | +- **PATCH** (0.2.1) - Bug fixes, backward compatible |
| 238 | + |
| 239 | +Examples: |
| 240 | +- `0.1.0` - Initial release |
| 241 | +- `0.2.0` - Added prefill sources and post-submission actions |
| 242 | +- `0.2.1` - Bug fix for database handler |
| 243 | +- `1.0.0` - First stable release with breaking changes |
| 244 | + |
| 245 | +## Security Notes |
| 246 | + |
| 247 | +1. **Never commit API tokens** to the repository |
| 248 | +2. **Use scoped tokens** - Limit token scope to the specific project |
| 249 | +3. **Rotate tokens** - Regenerate tokens periodically |
| 250 | +4. **Monitor usage** - Check PyPI for unexpected uploads |
| 251 | +5. **Enable 2FA** - Enable two-factor authentication on PyPI |
| 252 | + |
| 253 | +## Additional Resources |
| 254 | + |
| 255 | +- [PyPI Help](https://pypi.org/help/) |
| 256 | +- [Twine Documentation](https://twine.readthedocs.io/) |
| 257 | +- [GitHub Actions Documentation](https://docs.github.com/en/actions) |
| 258 | +- [Semantic Versioning](https://semver.org/) |
| 259 | +- [Keep a Changelog](https://keepachangelog.com/) |
| 260 | + |
| 261 | +## Support |
| 262 | + |
| 263 | +If you encounter issues: |
| 264 | +1. Check the GitHub Actions logs |
| 265 | +2. Review the PyPI upload history |
| 266 | +3. Consult the PyPI help documentation |
| 267 | +4. Open an issue in the repository |
| 268 | + |
0 commit comments