Skip to content

Commit ab4b849

Browse files
feat: use uv
Extras distributed via git (taskmanager, sentry) now require `uv` to install. Either use `uv` or `pip install` those manually (`uv pip install` works)
1 parent f8d095d commit ab4b849

10 files changed

Lines changed: 2970 additions & 502 deletions

File tree

.github/scripts/repack.sh

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

.github/workflows/ci.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
python-version: ${{ matrix.python-version }}
7070

7171
- name: Run tests
72-
run: uvx tox -e ${{ matrix.toxenv }}
72+
run: uvx --from tox-uv tox -e ${{ matrix.toxenv }}
7373

7474
- uses: codecov/codecov-action@v5
7575
with:
@@ -123,8 +123,8 @@ jobs:
123123
- name: Write SCM version
124124
id: scm_version
125125
run: |
126-
pipx install setuptools_scm
127-
echo "scm_version=$(setuptools-scm)" >> "$GITHUB_OUTPUT"
126+
pipx install hatch
127+
echo "scm_version=$(hatch version)" >> "$GITHUB_OUTPUT"
128128
129129
- name: Build and export
130130
uses: docker/build-push-action@v6
@@ -187,10 +187,18 @@ jobs:
187187
python-version: "3.12"
188188

189189
- name: Build Python distributions
190-
run: uvx --from build pyproject-build
190+
run: uv build
191191

192-
- name: Repack Python distributions
193-
run: bash .github/scripts/repack.sh
192+
- name: Strip git URLs from sdist (PyPI requirement)
193+
run: |
194+
WORK="$(mktemp -d)"
195+
TAR="$(ls dist/*.tar.*)"
196+
tar -C "$WORK" -xvaf "$TAR"
197+
sed -Ei 's/^(Requires-Dist:\s*[A-Za-z0-9][A-Za-z0-9._-]*)\s*@\s*([^ ]+)(.*)$/\1\3/' "$WORK"/*/PKG-INFO
198+
FN="$(ls "$WORK")"
199+
rm "$TAR"
200+
tar -C "$WORK" -cvaf "$TAR" "$FN"
201+
rm -rf "$WORK"
194202
195203
- name: Check Python distributions
196204
run: uvx twine check dist/*

Dockerfile

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,34 @@ WORKDIR /src
88
RUN npm install
99
RUN npm run production
1010

11-
FROM python:3.11-alpine AS backend
12-
13-
RUN apk add --no-cache build-base git linux-headers mariadb-dev
14-
15-
# Install dependencies before copying src, so pip is only run when needed
16-
# Also install build dependencies that are otherwise missed in wheel cache
17-
# (from pyproject.toml [build-system].requires)
18-
COPY ./requirements.txt ./setup.cfg /src/
19-
RUN cd /src && \
20-
pip install -U setuptools && \
21-
python -c "from setuptools.config import read_configuration as C; from itertools import chain; o=C('setup.cfg')['options']; ex=o['extras_require']; print('\0'.join(chain(o['install_requires'], ex['docker'], ex['sentry'], ex['server'], ex['taskmanager'])))" | xargs -0 pip wheel -q -c requirements.txt --wheel-dir /var/cache/wheels && \
22-
pip wheel -q --wheel-dir /var/cache/wheels wheel setuptools_scm[toml]
23-
2411
FROM python:3.11-alpine
2512

2613
ARG SETUPTOOLS_SCM_PRETEND_VERSION_FOR_FuzzManager
2714
ENV SETUPTOOLS_SCM_PRETEND_VERSION_FOR_FuzzManager=$SETUPTOOLS_SCM_PRETEND_VERSION_FOR_FuzzManager
2815

16+
# Install system dependencies
2917
RUN adduser -D worker && \
30-
apk add --no-cache bash git mariadb-client mariadb-connector-c openssh-client-default && \
31-
rm -rf /var/log/*
18+
apk add --no-cache bash git mariadb-client mariadb-connector-c openssh-client-default && \
19+
rm -rf /var/log/*
3220

33-
COPY --from=backend /var/cache/wheels /var/cache/wheels
34-
35-
COPY ./requirements.txt ./setup.cfg /src/
36-
USER worker
37-
RUN cd /src && \
38-
python -c "from setuptools.config import read_configuration as C; from itertools import chain; o=C('setup.cfg')['options']; ex=o['extras_require']; print('\0'.join(chain(o['install_requires'], ex['docker'], ex['sentry'], ex['server'], ex['taskmanager'])))" | xargs -0 pip install --no-cache-dir --no-index --find-links /var/cache/wheels -q -c requirements.txt
21+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
3922

4023
# Embed full source code
41-
USER root
4224
COPY . /src/
4325

4426
# Retrieve previous Javascript build
4527
COPY --from=frontend /src/dist/ /src/server/frontend/dist/
28+
29+
# Install build dependencies, install FuzzManager, then clean up in single layer
30+
RUN apk add --no-cache --virtual .build-deps build-base linux-headers mariadb-dev && \
31+
cd /src && \
32+
UV_PROJECT_ENVIRONMENT=/usr/local uv sync --frozen --extra docker --extra sentry --extra server --extra taskmanager --no-dev && \
33+
uv cache clean && \
34+
rm -rf /root/.cache/uv && \
35+
apk del .build-deps && \
36+
rm -rf /var/log/*
37+
38+
# Setup directories and permissions
4639
RUN mkdir -p \
4740
/data/fuzzing-tc-config \
4841
/data/crashes \
@@ -57,12 +50,8 @@ RUN mkdir -p \
5750
/data/repos \
5851
/data/userdata
5952

60-
# Install FM
61-
# Note: the extras must be duplicated above in the Python
62-
# script to pre-install dependencies.
6353
USER worker
6454
ENV PATH="${PATH}:/home/worker/.local/bin"
65-
RUN pip install --no-cache-dir --no-index --find-links /var/cache/wheels --no-deps -q /src[docker,sentry,server,taskmanager]
6655
RUN mkdir -m 0700 /home/worker/.ssh && cp /src/misc/sshconfig /home/worker/.ssh/config && ssh-keyscan github.com > /home/worker/.ssh/known_hosts
6756

6857
# Use a custom settings file that can be overwritten

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,21 @@ FuzzManager server instance or use FTB locally.
6767
The server part of FuzzManager is a Django application. Please note that it
6868
requires the full repository to be checked out, not just the server directory.
6969

70-
Dependency constraints are listed in [requirements.txt](requirements.txt). You can ask pip to respect these contraints by installing FuzzManager using:
70+
For reproducible server installations, use [uv](https://github.com/astral-sh/uv) with the lockfile:
7171

72-
```pip install -c requirements.txt '.[server]'```
72+
```bash
73+
# Install uv if needed
74+
curl -LsSf https://astral.sh/uv/install.sh | sh
75+
76+
# Install with locked dependencies
77+
uv sync --extra server --extra docker
78+
```
79+
80+
For a basic pip installation without locked versions:
81+
82+
```bash
83+
pip install '.[server]'
84+
```
7385

7486
A [Redis](https://redis.io/) server is also required for EC2SpotManager and API rate limiting, and can be installed on a Debian-based Linux with:
7587

pyproject.toml

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,153 @@
11
[build-system]
2-
requires = ["setuptools >= 43", "wheel", "setuptools_scm[toml] >= 3.4"]
3-
build-backend = "setuptools.build_meta"
2+
requires = ["hatchling", "hatch-vcs"]
3+
build-backend = "hatchling.build"
44

5+
[project]
6+
name = "FuzzManager"
7+
dynamic = ["version"]
8+
description = "A fuzzing management tools collection"
9+
readme = "README.md"
10+
requires-python = ">=3.10"
11+
license = {text = "MPL-2.0"}
12+
authors = [
13+
{name = "Christian Holler"},
14+
]
15+
maintainers = [
16+
{name = "Mozilla Fuzzing Team", email = "fuzzing@mozilla.com"},
17+
]
18+
keywords = ["fuzz", "fuzzing", "security", "test", "testing"]
19+
classifiers = [
20+
"Intended Audience :: Developers",
21+
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
22+
"Operating System :: OS Independent",
23+
"Programming Language :: Python",
24+
"Programming Language :: Python :: 3",
25+
"Programming Language :: Python :: 3.10",
26+
"Programming Language :: Python :: 3.11",
27+
"Programming Language :: Python :: 3.12",
28+
"Programming Language :: Python :: 3.13",
29+
"Programming Language :: Python :: 3.14",
30+
"Topic :: Software Development :: Testing",
31+
"Topic :: Security",
32+
]
33+
34+
# Core client dependencies (flexible ranges)
35+
dependencies = [
36+
"fasteners>=0.14.1",
37+
"requests>=2.20.1",
38+
]
39+
40+
[project.optional-dependencies]
41+
# Server dependencies (flexible compatible release ranges)
42+
server = [
43+
"celery~=5.3.5",
44+
"crispy-bootstrap3",
45+
"django~=4.2.7",
46+
"django-crispy-forms~=2.1",
47+
"django-enumfields~=2.1.1",
48+
"django-notifications-hq~=1.8.3",
49+
"djangorestframework~=3.15.1",
50+
"redis[hiredis]",
51+
"whitenoise~=6.9",
52+
]
53+
54+
# EC2 spot manager dependencies
55+
ec2spotmanager = [
56+
"boto3",
57+
"django-chartjs~=2.3.0",
58+
"laniakea",
59+
"pyyaml",
60+
]
61+
62+
# Task manager dependencies
63+
# Note: fuzzing-decision is a git dependency (see [tool.uv.sources])
64+
# uv will install it automatically, pip users need manual install
65+
taskmanager = [
66+
"fuzzing-decision",
67+
"kombu",
68+
]
69+
70+
# Docker/production dependencies
71+
docker = [
72+
"gunicorn~=23.0.0",
73+
"mercurial",
74+
"mozilla-django-oidc~=4.0.1",
75+
"mysqlclient~=2.2.4",
76+
]
77+
78+
# Sentry integration
79+
# Note: sentry-fuzzing-config is a git dependency (see [tool.uv.sources])
80+
# uv will install it automatically, pip users need manual install
81+
sentry = [
82+
"sentry-fuzzing-config",
83+
]
84+
85+
# Test dependencies
86+
test = [
87+
"fakeredis>=2.20.0; python_version <= '3.11'",
88+
"pytest",
89+
"pytest-cov",
90+
"pytest-django; python_version <= '3.11'",
91+
"pytest-mock",
92+
]
93+
94+
# Development dependencies
95+
dev = [
96+
"pre-commit",
97+
"tox",
98+
"tox-uv",
99+
]
100+
101+
[project.scripts]
102+
collector = "Collector:Collector.main"
103+
cov-reporter = "CovReporter:CovReporter.main"
104+
ec2-reporter = "EC2Reporter:EC2Reporter.main"
105+
task-status-reporter = "TaskStatusReporter:TaskStatusReporter.main"
106+
107+
[project.urls]
108+
Homepage = "https://github.com/MozillaSecurity/FuzzManager"
109+
Repository = "https://github.com/MozillaSecurity/FuzzManager"
110+
Issues = "https://github.com/MozillaSecurity/FuzzManager/issues"
111+
112+
# Hatchling build configuration
113+
[tool.hatch.version]
114+
source = "vcs"
115+
116+
[tool.hatch.build]
117+
packages = [
118+
"Collector",
119+
"CovReporter",
120+
"EC2Reporter",
121+
"FTB",
122+
"Reporter",
123+
"TaskStatusReporter",
124+
]
125+
126+
# Hatchling properly handles extras in wheels - no manual stripping needed!
127+
[tool.hatch.build.targets.wheel]
128+
packages = [
129+
"Collector",
130+
"CovReporter",
131+
"EC2Reporter",
132+
"FTB",
133+
"Reporter",
134+
"TaskStatusReporter",
135+
]
136+
137+
[tool.hatch.build.targets.sdist]
138+
exclude = [
139+
"/.github",
140+
"/.tox",
141+
"/dist",
142+
"*.pyc",
143+
"__pycache__",
144+
]
145+
146+
# Black configuration
5147
[tool.black]
6148
target-version = ["py310"]
7149

150+
# Coverage configuration
8151
[tool.coverage.report]
9152
exclude_lines = [
10153
"@(abc.)?abstract*",
@@ -22,6 +165,7 @@ omit = [
22165
"*/.eggs/*",
23166
]
24167

168+
# Isort configuration
25169
[tool.isort]
26170
known_first_party = [
27171
"Collector",
@@ -37,6 +181,7 @@ known_first_party = [
37181
]
38182
profile = "black"
39183

184+
# Pytest configuration
40185
[tool.pytest.ini_options]
41186
log_level = "DEBUG"
42187
DJANGO_SETTINGS_MODULE = "server.settings_test"
@@ -54,4 +199,12 @@ norecursedirs = [
54199
"dist",
55200
]
56201

57-
[tool.setuptools_scm]
202+
# UV configuration
203+
[tool.uv]
204+
# Git dependencies are kept in sources for development
205+
# They are not included in published packages (PyPI requirement)
206+
207+
[tool.uv.sources]
208+
# Keep git dependencies separate - these won't be in published packages
209+
fuzzing-decision = { git = "https://github.com/MozillaSecurity/orion", subdirectory = "services/fuzzing-decision" }
210+
sentry-fuzzing-config = { git = "https://github.com/MozillaSecurity/sentry" }

0 commit comments

Comments
 (0)