Skip to content

Commit 7ef58d6

Browse files
Pigbibicodex
andauthored
build: use uv dependency lock (#195)
Co-authored-by: Codex <noreply@openai.com>
1 parent 9d312f4 commit 7ef58d6

10 files changed

Lines changed: 1337 additions & 40 deletions

.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ __pycache__
77
*.py[cod]
88
.env
99
ft_cookies*.json
10+
build
11+
dist
12+
*.egg-info
13+
.venv-*
14+
.ruff_cache

.github/workflows/ci.yml

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,12 @@ jobs:
6464
- name: Install dependencies
6565
run: |
6666
set -euo pipefail
67-
python -m pip install --upgrade pip
68-
# Install non-git packages first to avoid cross-package dependency conflicts
69-
grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
70-
# Install git-based packages individually with --no-deps
71-
grep 'git+' requirements.txt | while IFS= read -r pkg; do
72-
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg"
73-
done
74-
python -m pip install pytest pytest-cov ruff build
75-
67+
python -m pip install --upgrade pip uv
68+
uv sync --frozen --extra test --no-install-project
7669
- name: Smoke import pinned shared packages
7770
run: |
7871
set -euo pipefail
79-
python - <<'PY'
72+
uv run --no-sync python - <<'PY'
8073
from quant_platform_kit.common.port_adapters import CallableNotificationPort, CallablePortfolioPort
8174
from us_equity_strategies import resolve_canonical_profile
8275
@@ -88,21 +81,21 @@ jobs:
8881
- name: Install editable shared repositories
8982
run: |
9083
set -euo pipefail
91-
python -m pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies
84+
uv pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies
9285
9386
- name: Verify Python dependencies
94-
run: python -m pip check
87+
run: uv pip check
9588

9689
- name: Run ruff
9790
run: |
9891
set -euo pipefail
99-
ruff check --exclude external .
92+
uv run --no-sync ruff check --exclude external .
10093
10194
- name: Run tests
102-
run: python -m pytest -q
95+
run: uv run --no-sync python -m pytest -q
10396

10497
- name: Check QPK pin consistency
105-
run: python scripts/check_qpk_pin_consistency.py
98+
run: uv run --no-sync python scripts/check_qpk_pin_consistency.py
10699

107100
- name: Build package
108-
run: python -m build
101+
run: uv run --no-sync python -m build

.github/workflows/sync-cloud-run-env.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,14 @@ jobs:
271271
if: steps.env_sync_config.outputs.enabled == 'true'
272272
run: |
273273
set -euo pipefail
274-
python -m pip install --upgrade pip
275-
grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
276-
grep 'git+' requirements.txt | while IFS= read -r pkg; do
277-
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg" -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
278-
done
279-
274+
python -m pip install --upgrade pip uv
275+
uv sync --frozen --no-dev --no-install-project
280276
- name: Resolve Cloud Run sync targets
281277
id: strategy_requirements
282278
if: steps.env_sync_config.outputs.enabled == 'true'
283279
run: |
284280
set -euo pipefail
285-
sync_plan_json="$(python scripts/build_cloud_run_env_sync_plan.py --json)"
281+
sync_plan_json="$(uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json)"
286282
{
287283
echo "sync_plan_json<<__SYNC_PLAN_JSON__"
288284
printf '%s\n' "${sync_plan_json}"

Dockerfile

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ FROM python:3.12-slim-bookworm
22

33
ENV PYTHONUNBUFFERED=1 \
44
PIP_NO_CACHE_DIR=1 \
5+
UV_COMPILE_BYTECODE=1 \
6+
UV_NO_CACHE=1 \
7+
PATH="/app/.venv/bin:${PATH}" \
58
PORT=8080
69

710
WORKDIR /app
@@ -10,21 +13,16 @@ RUN apt-get update \
1013
&& apt-get install -y --no-install-recommends git \
1114
&& rm -rf /var/lib/apt/lists/*
1215

13-
COPY requirements.txt ./
14-
COPY constraints.txt ./
15-
RUN python -m pip install --upgrade pip \
16-
&& grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c constraints.txt \
17-
&& grep "git+" requirements.txt | while IFS= read -r pkg; do \
18-
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg"; \
19-
done \
16+
COPY pyproject.toml uv.lock ./
17+
RUN python -m pip install --upgrade pip uv \
18+
&& uv sync --frozen --no-dev --no-install-project \
2019
&& apt-get purge -y git \
2120
&& apt-get autoremove -y --purge \
2221
&& rm -rf /var/lib/apt/lists/*
2322

24-
COPY . .
23+
RUN useradd --create-home --uid 1000 appuser
2524

26-
RUN useradd --create-home --uid 1000 appuser \
27-
&& chown -R appuser:appuser /app
25+
COPY --chown=appuser:appuser . .
2826
USER appuser
2927

3028
CMD ["gunicorn", "--bind", ":8080", "main:app"]

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ Direct runtime profiles can usually run from market history or portfolio state.
3838
## Quick start
3939

4040
```bash
41-
python -m pip install -e .
42-
python -m pytest -q
41+
uv sync --frozen --extra test --no-install-project
42+
uv run --no-sync ruff check --exclude external .
43+
uv run --no-sync python scripts/check_qpk_pin_consistency.py
4344
```
4445

4546
## Useful docs

README.zh-CN.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ FirstradePlatform 是 QuantStrategyLab 的实验性 Firstrade 执行平台。实
3838
## 快速开始
3939

4040
```bash
41-
python -m pip install -e .
42-
python -m pytest -q
41+
uv sync --frozen --extra test --no-install-project
42+
uv run --no-sync ruff check --exclude external .
43+
uv run --no-sync python scripts/check_qpk_pin_consistency.py
4344
```
4445

4546
## 延伸文档

pyproject.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,32 @@ version = "0.1.0"
88
description = "Experimental QuantStrategyLab platform layer for Firstrade through an unofficial API wrapper."
99
readme = "README.md"
1010
requires-python = ">=3.11"
11+
dependencies = [
12+
"firstrade==0.0.39",
13+
"flask",
14+
"google-auth",
15+
"google-cloud-storage",
16+
"gunicorn",
17+
"pandas_market_calendars",
18+
"pytest",
19+
"pytz",
20+
"requests",
21+
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0063af3b4a974650ea58a7d3f26dd1b94f65d3e8",
22+
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@46887bc3f5454d5b59623b1f5efb7c65912c6b8b",
23+
]
1124
license = "MIT"
1225
authors = [
1326
{ name = "QuantStrategyLab" }
1427
]
1528

29+
[project.optional-dependencies]
30+
test = [
31+
"pytest",
32+
"pytest-cov",
33+
"ruff",
34+
"build",
35+
]
36+
1637
[tool.setuptools]
1738
py-modules = [
1839
"decision_mapper",

tests/test_dependency_pin_guard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ def test_dependency_pin_guard_is_blocking_in_ci() -> None:
6161
next_step = workflow.find("\n - name:", step_start + 1)
6262
step = workflow[step_start : next_step if next_step != -1 else len(workflow)]
6363

64-
assert "python scripts/check_qpk_pin_consistency.py" in step
64+
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in step
6565
assert "continue-on-error" not in step
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from pathlib import Path
2+
3+
4+
def test_pyproject_declares_runtime_and_test_dependencies() -> None:
5+
pyproject = Path("pyproject.toml").read_text(encoding="utf-8")
6+
7+
assert "dependencies = [" in pyproject
8+
assert "quant-platform-kit @ git+https://github.com/QuantStrategyLab/" in pyproject
9+
assert "us-equity-strategies @ git+https://github.com/QuantStrategyLab/" in pyproject
10+
assert "[project.optional-dependencies]" in pyproject
11+
assert "test = [" in pyproject
12+
13+
14+
def test_ci_docker_and_env_sync_use_uv_lock() -> None:
15+
ci = Path(".github/workflows/ci.yml").read_text(encoding="utf-8")
16+
dockerfile = Path("Dockerfile").read_text(encoding="utf-8")
17+
env_sync = Path(".github/workflows/sync-cloud-run-env.yml").read_text(encoding="utf-8")
18+
lockfile = Path("uv.lock").read_text(encoding="utf-8")
19+
20+
assert lockfile.startswith("version = ")
21+
assert "uv sync --frozen --extra test --no-install-project" in ci
22+
assert "uv pip check" in ci
23+
assert "uv run --no-sync ruff check --exclude external ." in ci
24+
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in ci
25+
assert "uv run --no-sync python -m build" in ci
26+
assert "uv sync --frozen --no-dev --no-install-project" in env_sync
27+
assert "uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json" in env_sync
28+
assert "COPY pyproject.toml uv.lock ./" in dockerfile
29+
assert "uv sync --frozen --no-dev --no-install-project" in dockerfile
30+
assert "python -m pip install -r requirements.txt" not in dockerfile

0 commit comments

Comments
 (0)