Skip to content

Commit d699725

Browse files
authored
build: switch from pip to uv for dependency management (#416)
* 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> * fix: address PR review feedback for uv migration ## What Pin the uv Docker image to a versioned digest, restore unbuffered Python output in the container, and update the super-linter workflow to use uv instead of the deleted requirements files. ## Why The review identified three issues: supply-chain risk from using a mutable :latest tag, loss of unbuffered stdout/stderr behavior needed for GitHub Actions log streaming, and the super-linter workflow still referencing the removed requirements.txt files. ## Notes - uv image pinned to 0.10.9@sha256:10902f58... — will need Dependabot or manual updates to rotate - PYTHONUNBUFFERED=1 replaces the previous python3 -u entrypoint flag Signed-off-by: jmeridth <jmeridth@gmail.com> * fix: address super-linter CI failures ## What Fix zizmor bot-conditions audit and codespell false positive on uv.lock. ## Why The zizmor audit flagged github.actor as spoofable since it refers to the last actor to modify the PR, not the creator. Codespell flagged "astroid" (a real Python package) in uv.lock as a misspelling of "asteroid". ## Notes - Replaced github.actor with github.event.pull_request.user.login which refers to the PR creator and cannot be spoofed by later commits - Added .codespellrc to ignore-words-list for "astroid" Signed-off-by: jmeridth <jmeridth@gmail.com> * fix: exclude .venv from jscpd duplicate detection ## What Add .venv to jscpd ignore list in the linter configuration. ## Why The uv sync step creates a .venv in the workspace during CI. jscpd was scanning vendored C files inside mypyc and reporting 50.58% duplication over the 50% threshold, failing the super-linter check. ## Notes - This only became an issue after switching to uv, which creates .venv in the workspace rather than installing into the system Python Signed-off-by: jmeridth <jmeridth@gmail.com> --------- Signed-off-by: jmeridth <jmeridth@gmail.com>
1 parent 6f68596 commit d699725

14 files changed

Lines changed: 1064 additions & 92 deletions

.github/linters/.codespellrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[codespell]
2+
ignore-words-list = astroid

.github/linters/.jscpd.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"threshold": 50,
3-
"ignore": ["test*"],
3+
"ignore": ["test*", "**/.venv/**"],
44
"absolute": true
55
}

.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

.github/workflows/super-linter.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ jobs:
2222
with:
2323
fetch-depth: 0
2424
persist-credentials: false
25+
- name: Install uv
26+
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
27+
with:
28+
enable-cache: true
2529
- name: Install dependencies
26-
run: |
27-
python -m pip install --upgrade pip
28-
pip install -r requirements.txt -r requirements-test.txt
30+
run: uv sync
2931
- name: Lint Code Base
3032
uses: super-linter/super-linter@61abc07d755095a68f4987d1c2c3d1d64408f1f9 # v8.5.0
3133
env:
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.event.pull_request.user.login == '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: 7 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:0.10.9@sha256:10902f58a1606787602f303954cea099626a4adb02acbac4c69920fe9d278f82 /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/*
@@ -25,5 +27,7 @@ RUN python3 -m pip install --no-cache-dir --no-deps -r requirements.txt \
2527
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

30+
ENV PYTHONUNBUFFERED=1
31+
2832
CMD ["/action/workspace/contributors.py"]
29-
ENTRYPOINT ["python3", "-u"]
33+
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+
]

0 commit comments

Comments
 (0)