|
| 1 | +--- |
| 2 | +name: python-upgrade |
| 3 | +description: Audit and upgrade Python dependencies to fix vulnerabilities and apply patch-level updates, with test verification. |
| 4 | +allowed-tools: Bash, Read, Edit, Write, Agent, WebFetch, WebSearch |
| 5 | +user-invocable: true |
| 6 | +--- |
| 7 | + |
| 8 | +Our Python dependencies have vulnerabilities that we want to fix by upgrading. |
| 9 | + |
| 10 | +## Setup |
| 11 | + |
| 12 | +Switch to git branch "chore/upgrade", creating it if it doesn't exist. |
| 13 | +Start with `poetry sync --with dev` to be up to date with the lock file. |
| 14 | + |
| 15 | +## Auditing |
| 16 | + |
| 17 | +- Run `poetry run pip-audit` to list vulnerabilities. |
| 18 | +- Run `poetry show --outdated` to see packages that can be upgraded in general. |
| 19 | + |
| 20 | +## Upgrading |
| 21 | + |
| 22 | +- Use `poetry update <package-name>` to upgrade a dependency within the version range in pyproject.toml. |
| 23 | +- Use `poetry add <package>` to upgrade a package to something outside the version range in pyproject.toml. |
| 24 | +- Avoid code changes unless the fix is trivial (e.g., an import path change). If code changes are needed, note them in the summary. |
| 25 | +- Transitive dependencies can be upgraded if they have a security vulnerability. Note any upgraded transitive dependencies in the summary. |
| 26 | +- Ask before upgrading pinned versions. |
| 27 | + |
| 28 | +## Severity lookup |
| 29 | + |
| 30 | +Severity information for each vulnerability can be found like so https://github.com/advisories?query=CVE-2025-14009. Look up each advisory to determine its severity (critical, high, medium, low). |
| 31 | + |
| 32 | +## Upgrade strategy |
| 33 | + |
| 34 | +1. Run `pip-audit` and collect all vulnerabilities. |
| 35 | +2. Look up the severity of each vulnerability. |
| 36 | +3. Display a plan as a table showing each vulnerability with: package name, current version, fixed version, advisory ID, severity, depndency group (for exmaple "dev"), and the semantic version increase required. |
| 37 | +4. Sort the plan by: severity (critical first), then by smallest semantic version increase first. |
| 38 | +5. Perform all upgrades in the plan. |
| 39 | +6. Run `poetry run pytest` to see if the upgrades were successful. |
| 40 | +7. If all tests passed, perform all other outstanding upgrades if they are at patch level and within pyproject.toml ranges. |
| 41 | +8. Run `poetry run pytest` to see if all upgrades were successful. |
| 42 | +9. If any upgrade fails or if any test fails, roll back all changes with `git checkout -- poetry.lock pyproject.toml && poetry sync --with dev` and use the "Careful Upgrade Strategy". |
| 43 | + |
| 44 | +## Careful Upgrade Strategy |
| 45 | + |
| 46 | +1. Use this strategy if the general upgrade strategy fails. |
| 47 | +2. Use the plan created by the general upgrade strategy. |
| 48 | +3. Upgrade dependencies **one by one**, running `poetry run pytest` after each upgrade to ensure everything still works. |
| 49 | +4. If a test fails after an upgrade, revert that upgrade and move on to the next one. Note which upgrades were skipped and why. |
| 50 | +5. It might not be possible to upgrade all dependencies. Use your best effort. |
| 51 | + |
| 52 | +Think hard about dependency interactions and potential breaking changes. |
| 53 | + |
| 54 | +## Integration test |
| 55 | + |
| 56 | +After all achievable upgrades are complete, run a final integration test by starting the local server and sending a test GraphQL query: |
| 57 | + |
| 58 | +1. Run `yarn install` if node_modules are not present. |
| 59 | +2. Temporarily change `pythonBin: python3` to `pythonBin: python` in `serverless.yml` (needed for Windows compatibility). |
| 60 | +3. Start the server in the background, wait for it to be ready, then send a query: |
| 61 | + |
| 62 | +```bash |
| 63 | +ENABLE_METRICS=0 poetry run yarn sls wsgi serve > /tmp/server.log 2>&1 & |
| 64 | +SERVER_PID=$! |
| 65 | +``` |
| 66 | + |
| 67 | +Then in a separate command, poll the log file until the server is ready before sending the query. The server is ready when the log contains "Serving on". Retry up to 10 times with 3-second sleeps: |
| 68 | + |
| 69 | +```bash |
| 70 | +for i in $(seq 1 10); do |
| 71 | + if grep -q "Serving on" /tmp/server.log 2>/dev/null; then |
| 72 | + echo "Server is ready" |
| 73 | + break |
| 74 | + fi |
| 75 | + echo "Waiting for server... (attempt $i)" |
| 76 | + sleep 3 |
| 77 | +done |
| 78 | + |
| 79 | +cat /tmp/server.log |
| 80 | + |
| 81 | +curl -s -X POST http://localhost:5000/graphql \ |
| 82 | + -H "Content-Type: application/json" \ |
| 83 | + -d '{"query": "{ version about get_models { version } }"}' 2>&1 |
| 84 | +``` |
| 85 | + |
| 86 | +If the server log does not contain "Serving on" after all retries, print the log contents and report the failure instead of sending the query. |
| 87 | + |
| 88 | +Then clean up: |
| 89 | + |
| 90 | +```bash |
| 91 | +kill $SERVER_PID 2>/dev/null |
| 92 | +``` |
| 93 | + |
| 94 | +4. Verify the response contains valid `version`, `about`, and `get_models` data. |
| 95 | +5. Revert the `pythonBin` change in `serverless.yml` back to `python3`. |
| 96 | + |
| 97 | +## Bump version |
| 98 | + |
| 99 | +When done, create a commit and bump the version with |
| 100 | + |
| 101 | +```bash |
| 102 | +poetry run bump2version patch |
| 103 | +``` |
0 commit comments