diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0075fc..9d3b267 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: run: | set -euo pipefail python -m pip install --upgrade pip uv - uv sync --frozen --extra test --no-install-project + uv sync --frozen --extra test - name: Smoke import pinned shared packages run: | set -euo pipefail diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index 47052d8..5d3cdf7 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -272,7 +272,7 @@ jobs: run: | set -euo pipefail python -m pip install --upgrade pip uv - uv sync --frozen --no-dev --no-install-project + uv sync --frozen --no-dev - name: Resolve Cloud Run sync targets id: strategy_requirements if: steps.env_sync_config.outputs.enabled == 'true' diff --git a/.gitignore b/.gitignore index f1b12d1..f096dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ __pycache__/ .runtime/ ft_cookies*.json + +*.egg-info/ diff --git a/Dockerfile b/Dockerfile index 43dfcf1..0cd9382 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,16 +13,15 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends git \ && rm -rf /var/lib/apt/lists/* -COPY pyproject.toml uv.lock ./ +COPY . . RUN python -m pip install --upgrade pip uv \ - && uv sync --frozen --no-dev --no-install-project \ + && uv sync --frozen --no-dev \ && apt-get purge -y git \ && apt-get autoremove -y --purge \ - && rm -rf /var/lib/apt/lists/* - -RUN useradd --create-home --uid 1000 appuser + && rm -rf /var/lib/apt/lists/* \ + && useradd --create-home --uid 1000 appuser \ + && chown -R appuser:appuser /app -COPY --chown=appuser:appuser . . USER appuser CMD ["gunicorn", "--bind", ":8080", "main:app"] diff --git a/README.md b/README.md index 2a09ec1..39d5098 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Direct runtime profiles can usually run from market history or portfolio state. ## Quick start ```bash -uv sync --frozen --extra test --no-install-project +uv sync --frozen --extra test uv run --no-sync ruff check --exclude external . uv run --no-sync python scripts/check_qpk_pin_consistency.py ``` diff --git a/README.zh-CN.md b/README.zh-CN.md index 95699b4..b67b5e9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -38,7 +38,7 @@ FirstradePlatform 是 QuantStrategyLab 的实验性 Firstrade 执行平台。实 ## 快速开始 ```bash -uv sync --frozen --extra test --no-install-project +uv sync --frozen --extra test uv run --no-sync ruff check --exclude external . uv run --no-sync python scripts/check_qpk_pin_consistency.py ``` diff --git a/constraints.txt b/constraints.txt deleted file mode 100644 index 422acc7..0000000 --- a/constraints.txt +++ /dev/null @@ -1,10 +0,0 @@ -# QSL Dependency Constraints — SINGLE SOURCE OF TRUTH -# All repos MUST reference this file for git-based dependency SHAs. -# Generated: 2026-07-01 -# Auto-updated by update-qpk-pin.yml on every push to QPK main. - -quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0063af3b4a974650ea58a7d3f26dd1b94f65d3e8 -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@46887bc3f5454d5b59623b1f5efb7c65912c6b8b -hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@61993bf261aeccf64b5a75428b9405f4e1d1d682 -cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@f6c735c33047d7613a23d5df018ed32f394e6001 -crypto-strategies @ git+https://github.com/QuantStrategyLab/CryptoStrategies.git@de016240923727d60ba023fef345cebcfdae80df diff --git a/pyproject.toml b/pyproject.toml index bcf31d1..32967b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "firstrade-platform" -version = "0.2.0" +version = "0.3.0" description = "Experimental QuantStrategyLab platform layer for Firstrade through an unofficial API wrapper." readme = "README.md" requires-python = ">=3.11" @@ -50,6 +50,7 @@ include = [ "application*", "entrypoints*", "notifications*", + "scripts*", ] [tool.pytest.ini_options] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 744e41c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/scripts/gate_codex_app_review.py b/scripts/gate_codex_app_review.py index 0b9c47e..ef23e40 100644 --- a/scripts/gate_codex_app_review.py +++ b/scripts/gate_codex_app_review.py @@ -103,6 +103,8 @@ def compile_patterns(policy: dict[str, Any]) -> list[re.Pattern[str]]: # ─── static guard ──────────────────────────────────────────────────────────── +ALLOWED_REMOVED_FILES = {"constraints.txt", "requirements.txt"} + _SENSITIVE = re.compile( r'(?Papi[_\s]?key|secret|password|token|credential|private[_\s]?key)\s*[:=]\s*["\']' r'(?!\$\{\{|{{|example|placeholder|test|your[-_\s]|xxx|TODO|CHANGEME)[^"\']{12,}["\']', @@ -143,7 +145,7 @@ def check_metadata(files: list[dict[str, Any]], policy: dict[str, Any]) -> list[ for f in files: fn = f.get("filename", "?") st = (f.get("status") or "").lower().strip() - if st == "removed": + if st == "removed" and fn not in ALLOWED_REMOVED_FILES: issues.append(f"**File deleted**: `{fn}` — verify intentional") elif st == "renamed": issues.append(f"**File renamed**: `{f.get('previous_filename', '?')}` → `{fn}`") diff --git a/tests/test_gate_codex_app_review.py b/tests/test_gate_codex_app_review.py index 2a92cbe..fdfb252 100644 --- a/tests/test_gate_codex_app_review.py +++ b/tests/test_gate_codex_app_review.py @@ -1,6 +1,6 @@ from __future__ import annotations -from scripts.gate_codex_app_review import scan_diff +from scripts.gate_codex_app_review import check_metadata, scan_diff def test_scan_diff_redacts_hardcoded_secret_values() -> None: @@ -19,3 +19,18 @@ def test_scan_diff_redacts_hardcoded_secret_values() -> None: assert "" in violations[0] assert "api_key" in violations[0] assert secret_value not in violations[0] + + +def test_metadata_allows_removing_legacy_dependency_manifests() -> None: + files = [ + {"filename": "requirements.txt", "status": "removed"}, + {"filename": "constraints.txt", "status": "removed"}, + ] + + assert check_metadata(files, {}) == [] + + +def test_metadata_still_blocks_other_deleted_files() -> None: + files = [{"filename": "main.py", "status": "removed"}] + + assert check_metadata(files, {}) == ["**File deleted**: `main.py` — verify intentional"] diff --git a/tests/test_uv_dependency_workflow.py b/tests/test_uv_dependency_workflow.py index aaf3bcc..0beeab7 100644 --- a/tests/test_uv_dependency_workflow.py +++ b/tests/test_uv_dependency_workflow.py @@ -18,13 +18,17 @@ def test_ci_docker_and_env_sync_use_uv_lock() -> None: 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 sync --frozen --extra test" 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 sync --frozen --no-dev" 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 "COPY . ." in dockerfile + assert dockerfile.index("COPY . .") < dockerfile.index("uv sync --frozen --no-dev") + assert "uv sync --frozen --no-dev" in dockerfile assert "python -m pip install -r requirements.txt" not in dockerfile + assert "--no-install-project" not in ci + assert "--no-install-project" not in env_sync + assert "--no-install-project" not in dockerfile diff --git a/uv.lock b/uv.lock index 806d872..e9363f2 100644 --- a/uv.lock +++ b/uv.lock @@ -406,7 +406,7 @@ wheels = [ [[package]] name = "firstrade-platform" -version = "0.2.0" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "firstrade" },