Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ __pycache__
*.py[cod]
.env
ft_cookies*.json
build
dist
*.egg-info
.venv-*
.ruff_cache
25 changes: 9 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,12 @@ jobs:
- name: Install dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip
# Install non-git packages first to avoid cross-package dependency conflicts
grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
# Install git-based packages individually with --no-deps
grep 'git+' requirements.txt | while IFS= read -r pkg; do
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg"
done
python -m pip install pytest pytest-cov ruff build

python -m pip install --upgrade pip uv
uv sync --frozen --extra test --no-install-project

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Assert the uv.lock is current before syncing

When a dependency pin in pyproject.toml changes without regenerating uv.lock (for example a QPK/UES SHA bump), this CI step still installs the old locked package set: uv sync --help describes --frozen as syncing without updating uv.lock, not checking that it matches the project metadata. I verified a dry-run after changing only the pyproject ref still planned the old locked SHA, and the pin guard does not inspect uv.lock while this install runs before the guard; the Dockerfile and env-sync workflow use the same pattern, so tests/deploys can silently run against stale dependencies unless CI uses --locked or an explicit lock freshness check.

Useful? React with 👍 / 👎.

- name: Smoke import pinned shared packages
run: |
set -euo pipefail
python - <<'PY'
uv run --no-sync python - <<'PY'
from quant_platform_kit.common.port_adapters import CallableNotificationPort, CallablePortfolioPort
from us_equity_strategies import resolve_canonical_profile

Expand All @@ -88,21 +81,21 @@ jobs:
- name: Install editable shared repositories
run: |
set -euo pipefail
python -m pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies
uv pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies

- name: Verify Python dependencies
run: python -m pip check
run: uv pip check

- name: Run ruff
run: |
set -euo pipefail
ruff check --exclude external .
uv run --no-sync ruff check --exclude external .

- name: Run tests
run: python -m pytest -q
run: uv run --no-sync python -m pytest -q

- name: Check QPK pin consistency
run: python scripts/check_qpk_pin_consistency.py
run: uv run --no-sync python scripts/check_qpk_pin_consistency.py

- name: Build package
run: python -m build
run: uv run --no-sync python -m build
10 changes: 3 additions & 7 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,18 +271,14 @@ jobs:
if: steps.env_sync_config.outputs.enabled == 'true'
run: |
set -euo pipefail
python -m pip install --upgrade pip
grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
grep 'git+' requirements.txt | while IFS= read -r pkg; do
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg" -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
done

python -m pip install --upgrade pip uv
uv sync --frozen --no-dev --no-install-project
- name: Resolve Cloud Run sync targets
id: strategy_requirements
if: steps.env_sync_config.outputs.enabled == 'true'
run: |
set -euo pipefail
sync_plan_json="$(python scripts/build_cloud_run_env_sync_plan.py --json)"
sync_plan_json="$(uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json)"
{
echo "sync_plan_json<<__SYNC_PLAN_JSON__"
printf '%s\n' "${sync_plan_json}"
Expand Down
18 changes: 8 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ FROM python:3.12-slim-bookworm

ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
UV_COMPILE_BYTECODE=1 \
UV_NO_CACHE=1 \
PATH="/app/.venv/bin:${PATH}" \
PORT=8080

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

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

COPY . .
RUN useradd --create-home --uid 1000 appuser

RUN useradd --create-home --uid 1000 appuser \
&& chown -R appuser:appuser /app
COPY --chown=appuser:appuser . .
USER appuser

CMD ["gunicorn", "--bind", ":8080", "main:app"]
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ Direct runtime profiles can usually run from market history or portfolio state.
## Quick start

```bash
python -m pip install -e .
python -m pytest -q
uv sync --frozen --extra test --no-install-project
uv run --no-sync ruff check --exclude external .
uv run --no-sync python scripts/check_qpk_pin_consistency.py
```

## Useful docs
Expand Down
5 changes: 3 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ FirstradePlatform 是 QuantStrategyLab 的实验性 Firstrade 执行平台。实
## 快速开始

```bash
python -m pip install -e .
python -m pytest -q
uv sync --frozen --extra test --no-install-project
uv run --no-sync ruff check --exclude external .
uv run --no-sync python scripts/check_qpk_pin_consistency.py
```

## 延伸文档
Expand Down
21 changes: 21 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,32 @@ version = "0.1.0"
description = "Experimental QuantStrategyLab platform layer for Firstrade through an unofficial API wrapper."
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"firstrade==0.0.39",
"flask",
"google-auth",
"google-cloud-storage",
"gunicorn",
"pandas_market_calendars",
"pytest",
"pytz",
"requests",
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0063af3b4a974650ea58a7d3f26dd1b94f65d3e8",
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@46887bc3f5454d5b59623b1f5efb7c65912c6b8b",
]
license = "MIT"
authors = [
{ name = "QuantStrategyLab" }
]

[project.optional-dependencies]
test = [
"pytest",
"pytest-cov",
"ruff",
"build",
]

[tool.setuptools]
py-modules = [
"decision_mapper",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_dependency_pin_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ def test_dependency_pin_guard_is_blocking_in_ci() -> None:
next_step = workflow.find("\n - name:", step_start + 1)
step = workflow[step_start : next_step if next_step != -1 else len(workflow)]

assert "python scripts/check_qpk_pin_consistency.py" in step
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in step
assert "continue-on-error" not in step
30 changes: 30 additions & 0 deletions tests/test_uv_dependency_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from pathlib import Path


def test_pyproject_declares_runtime_and_test_dependencies() -> None:
pyproject = Path("pyproject.toml").read_text(encoding="utf-8")

assert "dependencies = [" in pyproject
assert "quant-platform-kit @ git+https://github.com/QuantStrategyLab/" in pyproject
assert "us-equity-strategies @ git+https://github.com/QuantStrategyLab/" in pyproject
assert "[project.optional-dependencies]" in pyproject
assert "test = [" in pyproject


def test_ci_docker_and_env_sync_use_uv_lock() -> None:
ci = Path(".github/workflows/ci.yml").read_text(encoding="utf-8")
dockerfile = Path("Dockerfile").read_text(encoding="utf-8")
env_sync = Path(".github/workflows/sync-cloud-run-env.yml").read_text(encoding="utf-8")
lockfile = Path("uv.lock").read_text(encoding="utf-8")

assert lockfile.startswith("version = ")
assert "uv sync --frozen --extra test --no-install-project" in ci
assert "uv pip check" in ci
assert "uv run --no-sync ruff check --exclude external ." in ci
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in ci
assert "uv run --no-sync python -m build" in ci
assert "uv sync --frozen --no-dev --no-install-project" in env_sync
assert "uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json" in env_sync
assert "COPY pyproject.toml uv.lock ./" in dockerfile
assert "uv sync --frozen --no-dev --no-install-project" in dockerfile
assert "python -m pip install -r requirements.txt" not in dockerfile
Loading
Loading