Skip to content

Commit d7e6bff

Browse files
committed
build: switch from pip to uv for dependency management
## What Replace pip-based dependency management with uv across the project, consolidating requirements.txt and requirements-test.txt into pyproject.toml with a generated uv.lock. Add a workflow to keep uv.lock in sync on Dependabot PRs. ## Why uv provides faster installs, deterministic lockfile resolution, and a simpler single-tool workflow for dependency and virtualenv management. ## Notes - Dockerfile now copies uv binary from ghcr.io/astral-sh/uv:latest and uses uv sync --frozen --no-dev instead of pip install - CI workflows use astral-sh/setup-uv with caching enabled - test_contributors.py reformatted by black (with-statement style change) - Dependabot will update pyproject.toml but does not natively understand uv.lock, so update-uv-lock.yml auto-commits the regenerated lockfile back to Dependabot PR branches - If branch protection requires signed commits, the update-uv-lock workflow may need a GitHub App token instead of GITHUB_TOKEN - Update CI matrix to include python 3.13 and 3.14 Signed-off-by: jmeridth <jmeridth@gmail.com>
1 parent 6f68596 commit d7e6bff

11 files changed

Lines changed: 1054 additions & 88 deletions

.github/workflows/copilot-setup-steps.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ jobs:
3030
with:
3131
persist-credentials: false
3232

33-
- name: Set up Python
34-
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
33+
- name: Install uv
34+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
3535
with:
36-
python-version: 3.12
36+
enable-cache: true
37+
38+
- name: Set up Python
39+
run: uv python install 3.14
3740

3841
- name: Install dependencies
39-
run: |
40-
pip install -r requirements.txt -r requirements-test.txt
42+
run: uv sync

.github/workflows/python-ci.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ jobs:
1818
runs-on: ubuntu-latest
1919
strategy:
2020
matrix:
21-
python-version: [3.11, 3.12]
21+
python-version: [3.11, 3.12, 3.13, 3.14]
2222
steps:
2323
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2424
with:
2525
persist-credentials: false
26-
- name: Set up Python ${{ matrix.python-version }}
27-
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
26+
- name: Install uv
27+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
2828
with:
29-
python-version: ${{ matrix.python-version }}
29+
enable-cache: true
30+
- name: Set up Python ${{ matrix.python-version }}
31+
run: uv python install ${{ matrix.python-version }}
3032
- name: Install dependencies
31-
run: |
32-
python -m pip install --upgrade pip
33-
pip install -r requirements.txt -r requirements-test.txt
33+
run: uv sync
3434
- name: Lint with flake8 and pylint
3535
run: |
3636
make lint
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
name: Update uv.lock
3+
4+
on:
5+
pull_request:
6+
paths:
7+
- pyproject.toml
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
update-lock:
15+
if: github.actor == 'dependabot[bot]'
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19+
with:
20+
ref: ${{ github.head_ref }}
21+
persist-credentials: true
22+
23+
- name: Install uv
24+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
25+
26+
- name: Update uv.lock
27+
run: uv lock
28+
29+
- name: Commit updated lockfile
30+
run: |
31+
git config user.name "github-actions[bot]"
32+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
33+
git add uv.lock
34+
git diff --cached --quiet || git commit -s -m "chore(deps): update uv.lock"
35+
git push

Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ LABEL com.github.actions.name="contributors" \
1414
org.opencontainers.image.description="GitHub Action that given an organization or repository, produces information about the contributors over the specified time period."
1515

1616
WORKDIR /action/workspace
17-
COPY requirements.txt *.py /action/workspace/
17+
COPY pyproject.toml uv.lock *.py /action/workspace/
1818

19-
RUN python3 -m pip install --no-cache-dir --no-deps -r requirements.txt \
19+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
20+
21+
RUN uv sync --frozen --no-dev --no-editable \
2022
&& apt-get -y update \
2123
&& apt-get -y install --no-install-recommends git=1:2.47.3-0+deb13u1 \
2224
&& rm -rf /var/lib/apt/lists/*
@@ -26,4 +28,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
2628
CMD python3 -c "import os,sys; sys.exit(0 if os.path.exists('/action/workspace/contributors.py') else 1)"
2729

2830
CMD ["/action/workspace/contributors.py"]
29-
ENTRYPOINT ["python3", "-u"]
31+
ENTRYPOINT ["uv", "run"]

Makefile

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.PHONY: test
22
test:
3-
pytest -v --cov=. --cov-config=.coveragerc --cov-fail-under=80 --cov-report term-missing
3+
uv run pytest -v --cov=. --cov-config=.coveragerc --cov-fail-under=80 --cov-report term-missing
44

55
.PHONY: clean
66
clean:
@@ -9,10 +9,10 @@ clean:
99
.PHONY: lint
1010
lint:
1111
# stop the build if there are Python syntax errors or undefined names
12-
flake8 . --config=.github/linters/.flake8 --count --select=E9,F63,F7,F82 --show-source
12+
uv run flake8 . --config=.github/linters/.flake8 --count --select=E9,F63,F7,F82 --show-source
1313
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
14-
flake8 . --config=.github/linters/.flake8 --count --exit-zero --max-complexity=15 --max-line-length=150
15-
isort --settings-file=.github/linters/.isort.cfg .
16-
pylint --rcfile=.github/linters/.python-lint --fail-under=9.0 *.py
17-
mypy --config-file=.github/linters/.mypy.ini *.py
18-
black .
14+
uv run flake8 . --config=.github/linters/.flake8 --count --exit-zero --max-complexity=15 --max-line-length=150
15+
uv run isort --settings-file=.github/linters/.isort.cfg .
16+
uv run pylint --rcfile=.github/linters/.python-lint --fail-under=9.0 *.py
17+
uv run mypy --config-file=.github/linters/.mypy.ini *.py
18+
uv run black .

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,12 @@ The job summary contains the same markdown content that is written to the config
266266
## Local usage without Docker
267267
268268
1. Make sure you have at least Python3.11 installed
269+
1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/)
269270
1. Copy `.env-example` to `.env`
270271
1. Fill out the `.env` file with a _token_ from a user that has access to the organization to scan (listed below). Tokens should have at least read:org access for organization scanning and read:repository for repository scanning.
271272
1. Fill out the `.env` file with the configuration parameters you want to use
272-
1. `pip3 install -r requirements.txt`
273-
1. Run `python3 ./contributors.py`, which will output everything in the terminal
273+
1. `uv sync`
274+
1. Run `uv run python3 ./contributors.py`, which will output everything in the terminal
274275
275276
## License
276277

pyproject.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[project]
2+
name = "contributors"
3+
version = "2.0.0"
4+
description = "GitHub Action that produces information about contributors over a specified time period."
5+
requires-python = ">=3.11"
6+
dependencies = [
7+
"github3-py==4.0.1",
8+
"python-dotenv==1.2.1",
9+
"requests==2.32.5",
10+
]
11+
12+
[dependency-groups]
13+
dev = [
14+
"black==26.1.0",
15+
"flake8==7.3.0",
16+
"mypy==1.19.1",
17+
"mypy-extensions==1.1.0",
18+
"pylint==4.0.5",
19+
"pytest==9.0.2",
20+
"pytest-cov==7.0.0",
21+
"types-requests==2.32.4.20260107",
22+
]

requirements-test.txt

Lines changed: 0 additions & 8 deletions
This file was deleted.

requirements.txt

Lines changed: 0 additions & 30 deletions
This file was deleted.

test_contributors.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -345,20 +345,21 @@ def test_main_sets_new_contributor_flag(self):
345345
"",
346346
)
347347

348-
with patch.object(
349-
contributors_module.env, "get_env_vars"
350-
) as mock_get_env_vars, patch.object(
351-
contributors_module.auth, "auth_to_github"
352-
) as mock_auth_to_github, patch.object(
353-
contributors_module, "get_all_contributors"
354-
) as mock_get_all_contributors, patch.object(
355-
contributors_module.contributor_stats,
356-
"is_new_contributor",
357-
return_value=True,
358-
) as mock_is_new, patch.object(
359-
contributors_module.markdown, "write_to_markdown"
360-
), patch.object(
361-
contributors_module.json_writer, "write_to_json"
348+
with (
349+
patch.object(contributors_module.env, "get_env_vars") as mock_get_env_vars,
350+
patch.object(
351+
contributors_module.auth, "auth_to_github"
352+
) as mock_auth_to_github,
353+
patch.object(
354+
contributors_module, "get_all_contributors"
355+
) as mock_get_all_contributors,
356+
patch.object(
357+
contributors_module.contributor_stats,
358+
"is_new_contributor",
359+
return_value=True,
360+
) as mock_is_new,
361+
patch.object(contributors_module.markdown, "write_to_markdown"),
362+
patch.object(contributors_module.json_writer, "write_to_json"),
362363
):
363364
mock_get_env_vars.return_value = (
364365
"org",
@@ -403,18 +404,19 @@ def test_main_fetches_sponsor_info_when_enabled(self):
403404
"https://github.com/sponsors/user1",
404405
)
405406

406-
with patch.object(
407-
contributors_module.env, "get_env_vars"
408-
) as mock_get_env_vars, patch.object(
409-
contributors_module.auth, "auth_to_github"
410-
) as mock_auth_to_github, patch.object(
411-
contributors_module, "get_all_contributors"
412-
) as mock_get_all_contributors, patch.object(
413-
contributors_module.contributor_stats, "get_sponsor_information"
414-
) as mock_get_sponsor_information, patch.object(
415-
contributors_module.markdown, "write_to_markdown"
416-
), patch.object(
417-
contributors_module.json_writer, "write_to_json"
407+
with (
408+
patch.object(contributors_module.env, "get_env_vars") as mock_get_env_vars,
409+
patch.object(
410+
contributors_module.auth, "auth_to_github"
411+
) as mock_auth_to_github,
412+
patch.object(
413+
contributors_module, "get_all_contributors"
414+
) as mock_get_all_contributors,
415+
patch.object(
416+
contributors_module.contributor_stats, "get_sponsor_information"
417+
) as mock_get_sponsor_information,
418+
patch.object(contributors_module.markdown, "write_to_markdown"),
419+
patch.object(contributors_module.json_writer, "write_to_json"),
418420
):
419421
mock_get_env_vars.return_value = (
420422
"org",

0 commit comments

Comments
 (0)