Add uv to CI#325
Conversation
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/python-app.yaml (2)
193-207: Make uploads idempotent and non-interactiveAvoid failures on re-runs when files already exist and ensure no prompts.
- name: Upload to TestPyPI run: | - twine upload --repository-url https://test.pypi.org/legacy/ dist/* --verbose + twine upload --repository-url https://test.pypi.org/legacy/ dist/* --verbose --skip-existing env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} + TWINE_NON_INTERACTIVE: "1"
279-293: Idempotent PyPI uploads and non-interactive modePrevents failures on retries and avoids prompts.
- name: Upload to PyPI run: | - twine upload dist/* --verbose + twine upload dist/* --verbose --skip-existing env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + TWINE_NON_INTERACTIVE: "1"
🧹 Nitpick comments (13)
.github/workflows/python-app.yaml (13)
35-39: Enable uv cache for faster, repeatable installsTurn on the built-in cache; replicate this across jobs for consistency.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
47-49: Prefer uvx for tools to avoid mutating the runner environmentRun Ruff via uvx instead of installing it.
- uv pip install --system "ruff==0.9.*" - ruff --version + uvx ruff --versionAlso update later Ruff invocations to:
uvx ruff check . --output-format=github uvx ruff format --check --diff .
75-79: Mirror: enable uv cache here as wellAlign with the lint job for better performance.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
87-88: Pin test-only tooling to a major/minor rangeImproves determinism across runs without over-constraining.
- uv pip install --system .[dev] pytest-xdist + uv pip install --system .[dev] "pytest-xdist==3.*"
113-114: Run Bandit via uvx; drop the explicit installKeeps the system interpreter clean and speeds up CI.
- uv pip install --system bandit[toml]Update the scan step to:
uvx bandit -r flixopt/ -c pyproject.toml -f json -o bandit-report.json -q -lll -ii uvx bandit -r flixopt/ -c pyproject.toml -q --exit-zero
145-148: Mirror: enable uv cache in create-releaseConsistent caching across jobs.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
183-187: Mirror: enable uv cache in publish-testpypiSame rationale as other jobs.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
209-228: Reduce flakiness and runtime in TestPyPI verify step
- Add strict bash options to fail fast.
- Consider capping total wait (current sum ≈ 42m) or fewer retries.
- name: Test install from TestPyPI if: env.SKIP_TESTPYPI_UPLOAD != 'true' run: | - # Create a temporary environment to test installation + set -Eeuo pipefail + # Create a temporary environment to test installation uv venv test_env source test_env/bin/activate @@ - # Install packaging for version comparison - uv pip install packaging + # Install packaging for version comparison + uv pip install "packaging==24.*" @@ - for d in 15 30 60 120 180 360 720 1080; do + for d in 15 30 60 120 240 360; do
235-237: Minor: prefer explicit timeouts on index callsSet a network timeout to avoid hanging on slow indexes.
- if uv pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" && \ + if uv pip --timeout 60 install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" && \
269-273: Mirror: enable uv cache in publish-pypiConsistent performance across jobs.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
297-323: Same verification hardening as TestPyPI stepApply strict bash options and cap retries.
- name: Verify PyPI installation run: | - # Create a temporary environment to test installation + set -Eeuo pipefail + # Create a temporary environment to test installation uv venv prod_test_env source prod_test_env/bin/activate @@ - # Install packaging for version comparison - uv pip install packaging + # Install packaging for version comparison + uv pip install "packaging==24.*" @@ - for d in 5 10 15 30 60 120 180 360 720 1080; do + for d in 5 10 15 30 60 120 240; do @@ - if uv pip install "$PACKAGE_NAME==$VERSION" && \ + if uv pip --timeout 60 install "$PACKAGE_NAME==$VERSION" && \
355-359: Mirror: enable uv cache in deploy-docsSpeed up docs build installs.
- name: Set up uv uses: astral-sh/setup-uv@v6 with: version: "0.8.17" + enable-cache: true
372-373: Avoid editable install for docs buildEditable isn’t needed in CI; improves isolation and speed.
- uv pip install --system -e ".[docs]" + uv pip install --system ".[docs]"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/python-app.yaml(10 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
🔇 Additional comments (1)
.github/workflows/python-app.yaml (1)
100-105: LGTM: uv caching enabledGood call enabling uv’s cache here. Consider enabling it in all uv setup steps for consistency.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (9)
.github/workflows/python-app.yaml (9)
35-40: Reduce repetition: centralize uv setup.
Set up uvis duplicated across jobs. Consider a composite action or a reusable workflow to DRY this.
194-199: Use uv build; drop unnecessary build deps.You can avoid preinstalling
build,setuptools, andwheel.Apply:
- uv pip install --system build setuptools wheel twine + uv pip install --system twine- python -m build + uv build
209-215: Make SKIP_TESTPYPI_UPLOAD explicit.The guard uses an undefined env var. Define a default for clarity/overrides.
Outside this hunk, add to the
publish-testpypijob:env: SKIP_TESTPYPI_UPLOAD: "false"
211-229: Remove unused dependency.
packagingisn’t used (version check usesimportlib.metadata). Drop it to speed up the job.Apply:
-# Install packaging for version comparison -uv pip install "packaging==24.*"
232-242: Tighten retry backoff to reduce idle time.Current waits sum to ~13.75 min. Consider shorter backoff unless you’ve observed long TestPyPI indexing.
Example:
-for d in 15 30 60 120 240 360; do +for d in 10 20 40 80 120; do
283-288: Mirror the uv build change here.Same as TestPyPI job—no need to preinstall build tooling.
Apply:
- uv pip install --system build twine + uv pip install --system twine- python -m build + uv build
315-317: Drop unused packaging install (PyPI job).Same rationale as TestPyPI section.
Apply:
-# Install packaging for version comparison -uv pip install "packaging==24.*"
320-330: Optional: align PyPI backoff with TestPyPI.If indexing is usually quick, consider the shorter backoff used above.
Example:
-for d in 5 10 15 30 60 120 240 360; do +for d in 10 20 40 80 120; do
359-364: Same uv setup duplication applies here.Consider the composite action/reusable workflow approach for uv setup.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/python-app.yaml(8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.13)
- GitHub Check: test (3.10)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
🔇 Additional comments (7)
.github/workflows/python-app.yaml (7)
48-60: Ruff via uvx looks good.Using
uvx ruffwith explicit version check and separate format check is clean.
75-85: Matrix + Python setup LGTM.Matrix across 3.10–3.13 with
actions/setup-python@v6is sound.
202-207: Twine config is correct.
--skip-existingandTWINE_NON_INTERACTIVE=1are good defaults for CI.
386-389: No action needed — docs extra already includes mike and mkdocs dependencies.
pyproject.toml [project.optional-dependencies] 'docs' lists "mkdocs-material >= 9.0.0, < 10", "mkdocstrings-python >= 1.0.0", and "mike >= 2.0.0".
377-377: Prefer lock + sync for reproducible installs (if using uv.lock)If the repo maintains a lock file, replace the unconstrained install step with a frozen sync:
run: uv pip install --system ".[docs]"
→
run: uv pip sync --system --frozen
File: .github/workflows/python-app.yaml (around line 377). Repo search returned no uv.lock — confirm presence of a lock file (fd -a '^uv.lock$' || true).
141-146: Fix uv version pin — 0.8.17 is not releasedLatest stable is 0.8.15 (released Sep 3, 2025).
- Update .github/workflows/python-app.yaml (lines 141–146): replace "0.8.17" with "0.8.15" or adopt an explicit update policy (exact pin or semver range like ^0.8).
- Verify CI commands (uv build, uvx, uv pip) for recent CI-affecting changes: UV_UNMANAGED_INSTALL / UV_ISOLATED, manylinux/--python-platform defaults, uvx tool/runtime management, new installer flags (--no-install-local, --no-editable, --all-packages), and ZIP hardening — adjust env vars/flags/caching as needed.
88-88: Keep explicit pytest-xdist install — not in .[dev]
pyproject.toml [project.optional-dependencies].dev contains "pytest" but not "pytest-xdist", so keep the workflow's explicit install.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
.github/workflows/python-app.yaml (2)
112-118: Align Bandit human-readable run with gated thresholds.Add the same severity/confidence filters to the non-gating run to avoid confusion between outputs.
- uvx bandit -r flixopt/ -c pyproject.toml -q --exit-zero + uvx bandit -r flixopt/ -c pyproject.toml --severity-level HIGH --confidence-level MEDIUM -q --exit-zero
127-133: Standardize the runner image.Use
ubuntu-22.04here to match the rest of the workflow unlessubuntu-latestis required for this job.- runs-on: ubuntu-latest + runs-on: ubuntu-22.04
🧹 Nitpick comments (5)
.github/workflows/python-app.yaml (5)
35-40: De-duplicate UV version; centralize as an env var.Avoid version drift and make bumps trivial by defining UV_VERSION once and referencing it in all “Set up uv” steps.
env: PYTHON_VERSION: "3.11" + UV_VERSION: "0.8.17" @@ - - name: Set up uv - uses: astral-sh/setup-uv@v6 - with: - version: "0.8.17" - enable-cache: true + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + version: ${{ env.UV_VERSION }} + enable-cache: trueApply the same change to all jobs’ UV setup steps shown in this workflow.
Also applies to: 75-80, 101-106, 141-146, 183-188, 269-274, 355-360
46-49: Ruff steps look good. Consider collapsing to reduce CI time.Optional: combine version check, lint, and format-check into a single step to save startup overhead.
Also applies to: 50-55, 56-61
176-178: Env flag name mismatch (upload vs verify).
SKIP_TESTPYPI_UPLOADonly gates the verify step, not the upload. Rename to avoid accidental misuse.- SKIP_TESTPYPI_UPLOAD: "false" + SKIP_TESTPYPI_VERIFY: "false" @@ - - name: Test install from TestPyPI - if: env.SKIP_TESTPYPI_UPLOAD != 'true' + - name: Test install from TestPyPI + if: env.SKIP_TESTPYPI_VERIFY != 'true'Also applies to: 210-214
194-201: Build with uv is fine; ensure reproducibility by pinning tool versions.
uv buildanduvxfetch latest tool versions at runtime. Consider pinning critical tools (e.g.,build,twine,ruff,bandit) for reproducible CI.Example:
-uv pip install --system twine +uv pip install --system "twine==5.*"and for CLIs:
-uvx ruff check . --output-format=github +uvx --from "ruff==0.6.*" ruff check . --output-format=githubAlso applies to: 280-287
229-241: Retry loop is sensible. Consider a capped total wait and logging of HTTP errors.Optional: cap total wait time and print
piperror summaries to aid debugging when indexing lags.-for d in 10 20 40 80 120; do +for d in 10 20 40 80 120; do # total wait: 4m50s sleep "$d" echo "Attempting to install $PACKAGE_NAME==$VERSION from TestPyPI (retry after ${d}s)..." - if uv pip --timeout 60 install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" && \ + if uv pip --timeout 60 install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" 2>install.err && \ python -c "from importlib.metadata import version; installed = version('$PACKAGE_NAME'); print(f'Installed: {installed}'); assert '$VERSION' == installed"; then INSTALL_SUCCESS=true break fi + tail -n 20 install.err || true doneApply analogously in the PyPI verification step.
Also applies to: 314-326
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/python-app.yaml(7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.10)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
🔇 Additional comments (3)
.github/workflows/python-app.yaml (3)
86-92: pytest invocation is consistent with uv --system installs.Direct pytest call is fine since deps are installed into the runner interpreter via
--system. No change needed.
341-386: Docs deploy chain is coherent. Ensure mike is included in.[docs].Assuming
.[docs]installsmike, this looks good. No changes if that’s true.Would you like me to scan
pyproject.tomlformikeunder thedocsextra and open an issue if missing?
25-27: Good use of per-job Python setup and global version pin.Consistent
actions/setup-python@v6usage with a singlePYTHON_VERSIONenv is clean.Also applies to: 41-45, 81-85, 107-111, 147-151, 189-193, 275-279, 361-365
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.github/workflows/python-app.yaml (1)
112-118: Make the human-readable Bandit run consistent with the gated run.The second Bandit command omits severity/confidence filters; add them to match the JSON run. (This was flagged earlier; re-raising.)
- uvx bandit -r flixopt/ -c pyproject.toml -q --exit-zero + uvx bandit -r flixopt/ -c pyproject.toml --severity-level high --confidence-level medium -q --exit-zero
🧹 Nitpick comments (5)
.github/workflows/python-app.yaml (5)
46-60: Pin tool versions for reproducible linting.
uvxpulls latest by default; pin Ruff to a major/minor to avoid surprise CI breaks.- uvx ruff --version + uvx --from "ruff==0.6.*" ruff --version @@ - uvx ruff check . --output-format=github + uvx --from "ruff==0.6.*" ruff check . --output-format=github @@ - uvx ruff format --check --diff . + uvx --from "ruff==0.6.*" ruff format --check --diff .
169-178: Don’t hard-code SKIP_TESTPYPI_UPLOAD; make it configurable and guard the upload step too.Allow overriding via repo variable and skip the upload when set.
environment: name: testpypi url: https://test.pypi.org/p/flixopt - env: - SKIP_TESTPYPI_UPLOAD: "false" + env: + SKIP_TESTPYPI_UPLOAD: ${{ vars.SKIP_TESTPYPI_UPLOAD || 'false' }} @@ - - name: Upload to TestPyPI + - name: Upload to TestPyPI + if: env.SKIP_TESTPYPI_UPLOAD != 'true' run: | twine upload --repository-url https://test.pypi.org/legacy/ dist/* --verbose --skip-existing @@ - - name: Test install from TestPyPI - if: env.SKIP_TESTPYPI_UPLOAD != 'true' + - name: Test install from TestPyPI + if: env.SKIP_TESTPYPI_UPLOAD != 'true'Also applies to: 202-205, 210-217
198-201: Clean dist/ before building to avoid stale artifacts.Prevent mixing leftovers from prior attempts in the same job.
- name: Build the distribution run: | - uv build + rm -rf dist/ + uv buildAlso applies to: 284-287
129-134: Standardize runner image for reproducibility.Consider aligning
create-releasewithubuntu-22.04like other jobs to reduce environment drift.- runs-on: ubuntu-latest + runs-on: ubuntu-22.04Also applies to: 141-146
46-49: Rename step for clarity.This step just prints the version; rename to avoid implying install.
- - name: Install Ruff + - name: Check Ruff version
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/python-app.yaml(7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.10)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
🔇 Additional comments (3)
.github/workflows/python-app.yaml (3)
35-40: UV action selection looks good.
astral-sh/setup-uv@v6is valid and supports pinning viaversionand cache usage. Keep as-is. (github.com)Also applies to: 75-80, 101-106, 141-146, 183-188, 269-274, 355-360
32-40: FYI: Other action versions used are current.
actions/checkout@v5andactions/upload-artifact@v4are the right majors as of today. No change needed. (github.com)Also applies to: 95-106, 141-146, 176-178, 269-274, 355-360
289-295: Switch to PyPI Trusted Publishers (OIDC) instead of storing PyPI API tokens.Replace the twine upload that uses TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} with PyPI Trusted Publishers (OIDC) to remove PYPI_API_TOKEN/TEST_PYPI_API_TOKEN management and reduce secret-leakage risk. I can draft the workflow + PyPI config steps. See PyPI Trusted Publishers and GitHub "Configuring OIDC in PyPI" for implementation details.
File: .github/workflows/python-app.yaml lines 289-295 (also applies to 203-209).
| - name: Set up Python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: ${{ env.PYTHON_VERSION }} | ||
| cache: 'pip' | ||
| cache-dependency-path: | | ||
| pyproject.toml | ||
|
|
There was a problem hiding this comment.
Use a valid setup-python version (v5). v6 does not exist.
actions/setup-python@v6 will 404 and break all jobs. Latest major is v5 (e.g., v5.5.0). Switch all occurrences to @v5. (github.com)
Apply:
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v5Do this for lines 42, 82, 108, 148, 190, 276, and 362.
Also applies to: 81-85, 107-111, 147-151, 189-193, 275-279, 361-365
🤖 Prompt for AI Agents
In .github/workflows/python-app.yaml around lines 41-45 (and also update
occurrences at 81-85, 107-111, 147-151, 189-193, 275-279, 361-365), the workflow
uses actions/setup-python@v6 which does not exist; replace each use reference to
actions/setup-python@v6 with actions/setup-python@v5 (or a specific v5 tag such
as v5.5.0) so the action resolves correctly in all the listed locations.
Description
Brief description of the changes in this PR.
Type of Change
Related Issues
Closes #(issue number)
Testing
Checklist
Summary by CodeRabbit
Chores
Tests
Security
Documentation