Repository navigation
196 lines (170 loc) · 7.08 KB
/
Copy pathci.yml
File metadata and controls
196 lines (170 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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
command -v sudo
command -v apt-get
sudo apt-get update
sudo apt-get install -y ripgrep
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