Skip to content

fix(deploy): release the Firstrade cached currency field #1313

fix(deploy): release the Firstrade cached currency field

fix(deploy): release the Firstrade cached currency field #1313

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Test Dot review safety contracts offline
run: node --test .github/dot-review/test-*.cjs
- name: Install public configuration scan tools
shell: bash
run: |
set -euo pipefail
if ! command -v rg >/dev/null; then
release="ripgrep-14.1.0-x86_64-unknown-linux-musl"
archive="$RUNNER_TEMP/$release.tar.gz"
curl --fail --silent --show-error --location --retry 2 --max-time 60 \
"https://github.com/BurntSushi/ripgrep/releases/download/14.1.0/$release.tar.gz" \
--output "$archive"
printf '%s %s\n' 'f84757b07f425fe5cf11d87df6644691c644a5cd2348a2c670894272999d3ba7' "$archive" | sha256sum --check --status
tar -xzf "$archive" -C "$RUNNER_TEMP" "$release/rg"
export PATH="$RUNNER_TEMP/$release:$PATH"
printf '%s\n' "$RUNNER_TEMP/$release" >> "$GITHUB_PATH"
fi
command -v rg
- name: Verify public configuration scanner contract offline
run: |
set -euo pipefail
python3 -m unittest discover -s tests -p test_public_runtime_configuration_scan.py -v
- name: Reject public runtime configuration bindings
id: public-config-scan
shell: bash
run: |
set -euo pipefail
command -v rg
if rg -l '\$\{\{[[:space:]]*vars\.(GLOBAL_TELEGRAM_CHAT_ID|CLOUD_RUN_SERVICES|CLOUD_RUN_SERVICE|RUNTIME_HEARTBEAT_REQUIRED_SERVICES|RUNTIME_GUARD_SCHEDULER_JOB_PATTERN)([[:space:]]|\}\}|\|\|)' .github/workflows; then
echo "Sensitive operational runtime configuration must use GitHub Secrets, not GitHub Variables." >&2
exit 1
else
status=$?
if [ "$status" -ne 1 ]; then
exit "$status"
fi
fi
printf '%s\n' 'scan_completed=true' >> "$GITHUB_OUTPUT"
echo "Public configuration scan completed without matches."
- name: Assert public configuration scan completed
if: ${{ always() }}
env:
PUBLIC_CONFIG_SCAN_OUTCOME: ${{ steps.public-config-scan.outcome }}
PUBLIC_CONFIG_SCAN_COMPLETED: ${{ steps.public-config-scan.outputs.scan_completed }}
run: |
set -euo pipefail
if [ "${PUBLIC_CONFIG_SCAN_OUTCOME:-}" != "success" ] ||
[ "${PUBLIC_CONFIG_SCAN_COMPLETED:-}" != "true" ]; then
echo "Public configuration scan completion was not verified." >&2
exit 1
fi
- name: Read locked shared dependency refs
id: locked-shared-refs
run: |
set -euo pipefail
python - <<'PY' >> "$GITHUB_OUTPUT"
import re
import tomllib
from pathlib import Path
required = {"quant-platform-kit", "us-equity-strategies"}
lock = tomllib.loads(Path("uv.lock").read_text(encoding="utf-8"))
refs = {}
for package in lock.get("package", []):
name = package.get("name")
if name not in required:
continue
source = package.get("source") or {}
match = re.search(r"#([0-9a-f]{40})$", str(source.get("git") or ""))
if match:
refs[name] = match.group(1)
if set(refs) != required:
raise SystemExit("uv.lock lacks a full SHA for a shared dependency")
for name in sorted(refs):
print(f"{name.replace('-', '_')}={refs[name]}")
PY
- name: Checkout QuantPlatformKit
uses: actions/checkout@v6
with:
repository: QuantStrategyLab/QuantPlatformKit
ref: ${{ steps.locked-shared-refs.outputs.quant_platform_kit }}
path: external/QuantPlatformKit
- name: Checkout UsEquityStrategies
uses: actions/checkout@v6
with:
repository: QuantStrategyLab/UsEquityStrategies
ref: ${{ steps.locked-shared-refs.outputs.us_equity_strategies }}
path: external/UsEquityStrategies
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip uv
uv sync --frozen --extra test
- name: Verify shared repository refs
run: |
set -euo pipefail
test "$(git -C external/QuantPlatformKit rev-parse HEAD)" = "${{ steps.locked-shared-refs.outputs.quant_platform_kit }}"
test "$(git -C external/UsEquityStrategies rev-parse HEAD)" = "${{ steps.locked-shared-refs.outputs.us_equity_strategies }}"
- name: Smoke import pinned shared packages
run: |
set -euo pipefail
uv run --no-sync python - <<'PY'
import importlib.metadata
import json
import tomllib
from pathlib import Path
lock = tomllib.loads(Path("uv.lock").read_text(encoding="utf-8"))
for package in lock["package"]:
if package["name"] not in {"quant-platform-kit", "us-equity-strategies"}:
continue
expected = package["source"]["git"].rsplit("#", 1)[1]
installed = json.loads(importlib.metadata.distribution(package["name"]).read_text("direct_url.json") or "{}")
assert installed.get("vcs_info", {}).get("commit_id") == expected, package["name"]
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
assert CallableNotificationPort
assert CallablePortfolioPort
assert resolve_canonical_profile("russell_top50_leader_rotation") == "russell_top50_leader_rotation"
PY
- name: Verify installed shared test imports
run: |
set -euo pipefail
uv run --no-sync python -m pytest -q -p test_support.pytest_provenance \
--shared-import-mode installed-lock tests/test_shared_import_contract.py
- name: Verify Python dependencies
run: uv pip check
- name: Run ruff
run: |
set -euo pipefail
uv run --no-sync ruff check --exclude external .
- name: Check QPK pin consistency
run: |
set -euo pipefail
printf '%s\n' "${{ steps.locked-shared-refs.outputs.quant_platform_kit }}" > "$RUNNER_TEMP/firstrade-qpk-pin"
uv run --no-sync python external/QuantPlatformKit/scripts/check_qpk_pin_consistency.py \
--root . \
--pin-file "$RUNNER_TEMP/firstrade-qpk-pin"
- name: Ensure uv.lock matches pyproject.toml
run: uv lock --check
- name: Run tests
run: uv run --no-sync python -m pytest -q
- name: Measure coverage against the production source layout
run: |
set -euo pipefail
uv run --no-sync python -m coverage run -m pytest -q
uv run --no-sync python -m coverage report --show-missing
uv run --no-sync python - <<'PY'
from coverage import CoverageData
data = CoverageData(basename=".coverage")
data.read()
files = sorted(data.measured_files())
if not files:
raise SystemExit("coverage measured no files")
for path in files:
print(path)
PY
- name: Build package
run: uv run --no-sync python -m build