Skip to content

Adopt uv across dev/CI/release workflows and remove remaining Poetry packaging usage#168

Open
Copilot wants to merge 11 commits into
mainfrom
copilot/add-uv-to-astral-tools
Open

Adopt uv across dev/CI/release workflows and remove remaining Poetry packaging usage#168
Copilot wants to merge 11 commits into
mainfrom
copilot/add-uv-to-astral-tools

Conversation

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

The project already uses Astral tools (ruff, ty) but still executed parts of development and release workflows through Poetry. This PR completes the migration to uv-based workflows and removes remaining Poetry packaging usage so linting, typing, testing, building, and publishing are aligned.

  • CI execution moved from Poetry to uv

    • Updated lint.yml to install/run tooling via uv:
      • uv sync --only-group dev --locked
      • uv run ruff check .
      • uv run ty check .
    • Updated test.yml to install/run test dependencies via uv:
      • uv sync --only-group test --locked
      • uv run pytest
    • Updated build.yml to build distributions with uv build.
  • Devcontainer tooling aligned with uv

    • Replaced Poetry devcontainer feature with the uv feature.
    • Switched postCreateCommand from poetry install to lockfile-based sync:
      • uv sync --all-groups --locked
  • Lockfile and metadata for reproducible environments

    • Added and committed uv.lock.
    • Added [project] and [dependency-groups] metadata in pyproject.toml so uv can lock/sync dev and test groups.
  • Packaging backend migrated off Poetry

    • Removed legacy [tool.poetry*] sections from pyproject.toml.
    • Switched [build-system] from poetry-core to hatchling.
    • Added hatch wheel target configuration for packaging the pyrail module.
  • Release publishing workflow migrated from Poetry to uv

    • Updated publish.yml to use:
      • uv sync --locked
      • uv build
      • uv publish
    • Replaced poetry version/lock with a guarded inline update of [project].version from the tag-derived VERSION.
    • Added validation for missing/invalid semver VERSION and strict single-substitution checks before committing the version bump.
  • Developer docs updated to match new commands

    • Updated README development commands to use uv run ... for:
      • tests
      • Ruff checks/fixes
      • ty type checks
    • Added README setup step for lockfile sync:
      • uv sync --all-groups --locked
    • Updated contributing guidance to reference uv run ruff check ..
uv sync --all-groups --locked
uv run ruff check .
uv run ty check .
uv run pytest
uv build

[!WARNING]

Summary by CodeRabbit

  • Chores

    • Migrated to modernized build and dependency management tooling for faster installations, reduced build time, and improved development experience
    • Updated development container, CI/CD automation pipelines, and build configuration to align with new tooling
  • Documentation

    • Updated development documentation to reflect new environment setup, testing, and workflow commands

Review Change Stack

Copilot AI linked an issue May 13, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add uv to complete Astral ecosystem Adopt uv for Astral-aligned dev/CI workflows May 13, 2026
Copilot finished work on behalf of tjorim May 13, 2026 18:59
Copilot AI requested a review from tjorim May 13, 2026 18:59
@tjorim

tjorim commented May 13, 2026

Copy link
Copy Markdown
Owner

@copilot why do we use uv pip? I also don't see the lock file being committed

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot why do we use uv pip? I also don't see the lock file being committed

Good call. I switched this to lockfile-based uv sync (no more uv pip in workflows/devcontainer) and committed uv.lock for reproducible installs in cce15bd.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.irail.be
    • Triggering command: /home/REDACTED/work/pyrail/pyrail/.venv/bin/pytest pytest -k successful_request or irail_context_manager or date_time_validation or error_handling or timestamp_to_datetime or timestamp_field_deserialization or str_to_bool or boolean_field_deserialization (dns block)
    • Triggering command: /home/REDACTED/work/pyrail/pyrail/.venv/bin/pytest pytest (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Adopt uv for Astral-aligned dev/CI workflows Adopt uv with lockfile-based sync for Astral-aligned dev/CI workflows May 13, 2026
Copilot finished work on behalf of tjorim May 13, 2026 19:04
@tjorim

tjorim commented May 13, 2026

Copy link
Copy Markdown
Owner

@copilot any reason we still have poetry in our pyproject file?

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot any reason we still have poetry in our pyproject file?

Yes — right now Poetry is still the package backend (poetry-core in [build-system]) and is also used by the publish.yml release workflow (poetry version/lock/publish). We migrated dev/CI execution paths to uv, but publishing/backend metadata is still Poetry-based.

Copilot finished work on behalf of tjorim May 13, 2026 19:07
@tjorim

tjorim commented May 13, 2026

Copy link
Copy Markdown
Owner

@copilot so... Why not switch that as well? We can't do that with UV?

Copilot AI and others added 8 commits May 13, 2026 19:11

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot so... Why not switch that as well? We can't do that with UV?

Yes, we can — and I’ve switched it. Poetry has now been removed from pyproject.toml (backend moved to Hatchling) and the publish workflow was migrated to uv (uv sync, uv build, uv publish) in 7e78d7b.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.irail.be
    • Triggering command: /home/REDACTED/work/pyrail/pyrail/.venv/bin/pytest pytest (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Adopt uv with lockfile-based sync for Astral-aligned dev/CI workflows Adopt uv across dev/CI/release workflows and remove remaining Poetry packaging usage May 13, 2026
Copilot finished work on behalf of tjorim May 13, 2026 19:17
@tjorim tjorim marked this pull request as ready for review May 13, 2026 19:25
@tjorim

tjorim commented May 13, 2026

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR migrates the entire pyrail project from Poetry to Astral's uv for dependency management and packaging. Changes span pyproject.toml (PEP 621 + Hatch), all GitHub Actions workflows, the devcontainer, and documentation to use uv commands consistently.

Changes

Poetry to uv migration

Layer / File(s) Summary
Package configuration: PEP 621 and Hatch migration
pyproject.toml
Metadata and runtime dependencies migrate from [tool.poetry] tables to PEP 621 [project] and [dependency-groups]; build backend switches from poetry.core to hatchling with wheel configuration.
Development environment setup with uv
.devcontainer/devcontainer.json
Devcontainer adds uv feature and replaces postCreateCommand from poetry install to uv sync --all-groups --locked.
Build and test workflows migrated to uv
.github/workflows/build.yml, .github/workflows/test.yml
Both workflows add setup-uv step, replace poetry install with uv sync, and run package build and pytest via uv instead of Poetry.
Lint workflow updated for uv
.github/workflows/lint.yml
Adds setup-uv, replaces poetry run with uv sync --only-group dev --locked and uv run for Ruff and Ty checks.
Release workflow with automated version bumping and uv publish
.github/workflows/publish.yml
Adds setup-uv, introduces inline Python script to validate VERSION env var against semantic-version regex and update pyproject.toml, commits and publishes using uv build and uv publish with UV_PUBLISH_TOKEN.
Documentation updates for uv commands
README.md
Development and contributing sections updated to show uv sync, uv run pytest, and uv run ruff/uv run ty commands.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tjorim/pyrail#37: Both PRs modify .github/workflows/publish.yml around dependency installation and publishing logic, with this PR replacing Poetry workflow with uv equivalents.

Suggested reviewers

  • tjorim

Poem

A rabbit hops through Poetry's door
And emerges on uv's shore
With deps aligned and builds so fast,
The migration sprint is complete at last! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective: migrating from Poetry to uv across development, CI, and release workflows while removing Poetry packaging configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/add-uv-to-astral-tools

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/lint.yml:
- Line 26: Replace the `uv sync` invocation that currently uses the
`--only-group dev` flag with `--group dev` so production dependencies are
included during type checking; specifically update the command string `uv sync
--only-group dev --locked` to `uv sync --group dev --locked` in the workflow
step that runs the type-check/lint job.

In @.github/workflows/publish.yml:
- Around line 62-68: The workflow step "Push version bump to main" should skip
committing/pushing when there are no staged changes; replace the unconditional
git commit/git push sequence by first checking the staged diff (e.g., using git
diff --staged --quiet or git diff --cached --quiet) and only run git commit -m
"Bump version to $VERSION" and git push origin HEAD:main if that check indicates
there are changes; keep the existing git config and git add pyproject.toml but
guard the commit/push behind the staged-diff check so the job continues when the
version rewrite is a no-op.

In @.github/workflows/test.yml:
- Line 26: The workflow currently runs "uv sync --only-group test --locked",
which installs only test dependencies and omits the package and its runtime
deps; update the GitHub Actions step to use "uv sync --group test --locked" so
the package itself and its runtime dependencies (e.g., aiohttp,
mashumaro[orjson]) are installed alongside the test group, preventing import
errors during tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 24ce3ad0-04da-47e6-8090-500347226fd6

📥 Commits

Reviewing files that changed from the base of the PR and between 10f326c and 7e78d7b.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .devcontainer/devcontainer.json
  • .github/workflows/build.yml
  • .github/workflows/lint.yml
  • .github/workflows/publish.yml
  • .github/workflows/test.yml
  • README.md
  • pyproject.toml

run: |
pip install poetry
poetry install
run: uv sync --only-group dev --locked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the project configuration files
fd -e "pyproject.toml" -e "uv.lock" -e "requirements.txt" | head -20

Repository: tjorim/pyrail

Length of output: 39


🏁 Script executed:

#!/bin/bash
# Look at the lint.yml workflow file
cat .github/workflows/lint.yml

Repository: tjorim/pyrail

Length of output: 593


🏁 Script executed:

#!/bin/bash
# Check if there's a test.yml mentioned in the review comment
cat .github/workflows/test.yml 2>/dev/null | head -60

Repository: tjorim/pyrail

Length of output: 530


🏁 Script executed:

#!/bin/bash
# List all files in the repo to find pyproject.toml
git ls-files | grep -E "(pyproject|setup\.py|uv\.lock)" | head -20

Repository: tjorim/pyrail

Length of output: 80


🏁 Script executed:

#!/bin/bash
# List root directory
ls -la

Repository: tjorim/pyrail

Length of output: 1199


🏁 Script executed:

#!/bin/bash
# Read pyproject.toml
cat -n pyproject.toml

Repository: tjorim/pyrail

Length of output: 2061


🏁 Script executed:

#!/bin/bash
# Check if pyrail source code imports aiohttp or mashumaro
rg "(import aiohttp|from aiohttp|import mashumaro|from mashumaro)" pyrail/

Repository: tjorim/pyrail

Length of output: 263


Replace --only-group dev with --group dev to include production dependencies during type checking.

The --only-group dev flag installs only the dev group (ruff, ty) and the package itself, excluding production dependencies. However, pyrail imports runtime dependencies (aiohttp and mashumaro) in its source code. Type checking with ty requires these dependencies to be available for import resolution; without them, type checking will fail or produce incomplete results.

Use uv sync --group dev --locked instead to include both production and dev dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/lint.yml at line 26, Replace the `uv sync` invocation that
currently uses the `--only-group dev` flag with `--group dev` so production
dependencies are included during type checking; specifically update the command
string `uv sync --only-group dev --locked` to `uv sync --group dev --locked` in
the workflow step that runs the type-check/lint job.

Comment on lines 62 to 68
- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml poetry.lock
git add pyproject.toml
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Skip the commit when the version rewrite is a no-op.

If the tag already matches [project].version, git commit fails with “nothing to commit” and the release stops before uv publish runs. Guard the commit/push behind a staged-diff check.

Proposed fix
     - name: Push version bump to main
       run: |
         git config user.name "GitHub Actions"
         git config user.email "actions@github.com"
         git add pyproject.toml
-        git commit -m "Bump version to $VERSION"
-        git push origin HEAD:main  # Push from detached HEAD directly to main
+        if git diff --cached --quiet; then
+          echo "pyproject.toml already matches $VERSION; skipping commit"
+        else
+          git commit -m "Bump version to $VERSION"
+          git push origin HEAD:main  # Push from detached HEAD directly to main
+        fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml poetry.lock
git add pyproject.toml
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main
- name: Push version bump to main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add pyproject.toml
if git diff --cached --quiet; then
echo "pyproject.toml already matches $VERSION; skipping commit"
else
git commit -m "Bump version to $VERSION"
git push origin HEAD:main # Push from detached HEAD directly to main
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 62 - 68, The workflow step "Push
version bump to main" should skip committing/pushing when there are no staged
changes; replace the unconditional git commit/git push sequence by first
checking the staged diff (e.g., using git diff --staged --quiet or git diff
--cached --quiet) and only run git commit -m "Bump version to $VERSION" and git
push origin HEAD:main if that check indicates there are changes; keep the
existing git config and git add pyproject.toml but guard the commit/push behind
the staged-diff check so the job continues when the version rewrite is a no-op.

run: |
pip install poetry
poetry install
run: uv sync --only-group test --locked

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if pyrail declares runtime dependencies in pyproject.toml

echo "=== Checking for runtime dependencies in [project.dependencies] ==="
if grep -A 20 '^\[project\.dependencies\]' pyproject.toml | grep -v '^#' | grep -v '^\[' | grep -q '^\S'; then
  echo "✗ Runtime dependencies found:"
  grep -A 20 '^\[project\.dependencies\]' pyproject.toml | grep -v '^#' | grep -v '^\[' | grep '^\S'
  echo ""
  echo "CONCERN: --only-group test may exclude these dependencies, breaking tests."
else
  echo "✓ No runtime dependencies declared"
fi

echo ""
echo "=== Checking for runtime dependencies in [project] dependencies array ==="
python3 << 'EOF'
import tomllib
with open('pyproject.toml', 'rb') as f:
    data = tomllib.load(f)
    deps = data.get('project', {}).get('dependencies', [])
    if deps:
        print(f"✗ Runtime dependencies found: {deps}")
        print("\nCONCERN: --only-group test may exclude these dependencies, breaking tests.")
    else:
        print("✓ No runtime dependencies in dependencies array")
EOF

Repository: tjorim/pyrail

Length of output: 402


🏁 Script executed:

cat pyproject.toml

Repository: tjorim/pyrail

Length of output: 1620


🏁 Script executed:

# Check the structure of dependency groups and how they're defined
python3 << 'EOF'
import tomllib
with open('pyproject.toml', 'rb') as f:
    data = tomllib.load(f)
    
print("=== Project Dependencies (runtime) ===")
runtime_deps = data.get('project', {}).get('dependencies', [])
print(runtime_deps if runtime_deps else "None")

print("\n=== Dependency Groups ===")
dep_groups = data.get('dependency-groups', {})
if dep_groups:
    for group_name, deps in dep_groups.items():
        print(f"\n[{group_name}]")
        for dep in deps:
            print(f"  {dep}")
else:
    print("No dependency-groups found")

print("\n=== Check for [project.optional-dependencies] ===")
opt_deps = data.get('project', {}).get('optional-dependencies', {})
if opt_deps:
    for group_name, deps in opt_deps.items():
        print(f"\n[{group_name}]")
        for dep in deps:
            print(f"  {dep}")
else:
    print("None")
EOF

Repository: tjorim/pyrail

Length of output: 385


🌐 Web query:

uv sync --only-group behavior local package dependencies

💡 Result:

In uv, uv sync --only-group &lt;name&gt; installs only the dependencies from the specified dependency group(s), omitting the project itself. The semantics match --only-dev: “the project will not be included” and --only-group also excludes default groups [1][2].\n\nHowever, when your dependency graph includes local package dependencies, uv may still need to evaluate/build packages outside the selected group during resolution/lockfile creation, because it must compute dependencies transitively and/or build metadata. uv’s own maintainers and issue reports indicate that even when a group is excluded, uv can still fetch/build to compute dependencies (and those packages may be “downloaded and tried to build” even if not ultimately installed) [3][4][5].\n\nKey takeaways for “local package dependencies”:\n1) “Only-group” controls what ends up installed; it does not necessarily prevent uv from resolving/inspecting other parts of the dependency graph during lockfile/resolution.\n- uv’s docs describe exclusion of the project itself, and exclusion of default groups for --only-group [1][2].\n- Issue reports show cases where uv still evaluates or downloads/builds packages from excluded groups or sources while computing dependency information [3][5].\n\n2) If you want to avoid re-resolving/evaluating after the lockfile is created, use --frozen (or otherwise rely on an up-to-date uv.lock). Maintainers note that uv will resolve to update the lockfile unless you freeze it [6].\n\nPractical guidance:\n- If you run uv sync --only-group &lt;grp&gt; and you observe local-path dependencies getting built/evaluated that “shouldn’t be there,” that is likely part of resolution/metadata evaluation, not necessarily final installation.\n- For CI/docker-style installs where you want deterministic behavior and minimal evaluation, generate uv.lock once, then use uv sync --frozen --only-group &lt;grp&gt;.

Citations:


Use uv sync --group test --locked to include the package and its runtime dependencies.

The --only-group test flag installs only the test dependency group while omitting the project itself and its runtime dependencies. Since pyrail declares runtime dependencies (aiohttp>=3.13.5,<4.0.0 and mashumaro[orjson]>=3.21,<4.0.0), the tests will fail with import errors when the package tries to import these modules.

Change to uv sync --group test --locked to ensure both the package and its runtime dependencies are installed alongside the test dependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml at line 26, The workflow currently runs "uv sync
--only-group test --locked", which installs only test dependencies and omits the
package and its runtime deps; update the GitHub Actions step to use "uv sync
--group test --locked" so the package itself and its runtime dependencies (e.g.,
aiohttp, mashumaro[orjson]) are installed alongside the test group, preventing
import errors during tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

astral tools

2 participants