Repository navigation
202 lines (181 loc) · 7.49 KB
/
Copy pathparity.yml
File metadata and controls
202 lines (181 loc) · 7.49 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
197
198
199
200
201
202
# Three guarantees. This workflow turns "the install paths have drifted" from
# something discovered by reading code into a number that moves.
#
# 1. locks — the hash-pinned lock matches requirements.txt everywhere
# 2. parity — pip and conda installs produce the same environment
# 3. installer-e2e — an install produces the same thing as a checkout
#
# A lock can only be generated on the platform it describes, so they are
# generated by hand and committed. CI only verifies they are current.
#
# There is no frozen-agent job: the agent is no longer a PyInstaller bundle,
# which is what removed the whole class of divergence these jobs guard.
name: Install parity
on:
# No push trigger: nothing here writes to the repo, so there is nothing a
# push would accomplish that a PR does not.
pull_request:
paths:
- "requirements.txt"
- "environment.yml"
- "requirements/**"
- "scripts/generate_lock.py"
- "scripts/parity_check.py"
- "scripts/package_source.py"
- "scripts/test_install_e2e.py"
- "app/provision/**"
- "app/paths.py"
- ".github/workflows/parity.yml"
workflow_dispatch:
jobs:
# ──────────────────────────────────────────────
# Every committed lock came from the current
# requirements.txt.
# ──────────────────────────────────────────────
locks:
name: Locks match requirements.txt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
# Checks all platforms' locks from one runner, and needs no pip resolve:
# it compares the source digest recorded in each lock's header. See
# scripts/generate_lock.py for why re-resolving here would be wrong.
#
# This only CHECKS. Locks are generated by hand — `python
# scripts/generate_lock.py` — on the platform each one describes, and
# committed. release.yml refuses to build a payload if one is missing.
- name: Check locks
run: python scripts/generate_lock.py --check
# ──────────────────────────────────────────────
# pip vs conda must produce the same environment.
# environment.yml used to carry its own package
# list, which drifted by 15 packages. It now
# provides only the runtime, and BOTH paths
# install the same lock — so this job checks
# that the two runtimes agree, not two lists.
# ──────────────────────────────────────────────
parity:
name: pip vs conda (${{ matrix.os_label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
os_label: linux
- os: windows-latest
os_label: windows
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install via pip (from the lock)
shell: bash
run: |
python -m venv .venv-pip
if [ -f .venv-pip/bin/python ]; then PY=.venv-pip/bin/python;
else PY=.venv-pip/Scripts/python.exe; fi
echo "PIP_PY=$PY" >> "$GITHUB_ENV"
"$PY" -m pip install --upgrade pip
# `ls | head -1` would pick another platform's lock once Linux and
# macOS locks exist. app.provision.deps.find_lock resolves the exact
# (platform, python) tag and refuses to fall back — a Linux lock
# pins CUDA-flavoured torch wheels that do not exist on Windows.
LOCK=$("$PY" -c "from app.provision.deps import find_lock; print(find_lock('.') or '')")
if [ -z "$LOCK" ]; then
echo "::error::no lock for this platform — run scripts/generate_lock.py"
exit 1
fi
echo "using $LOCK"
"$PY" -m pip install --require-hashes -r "$LOCK"
- name: Fingerprint pip install
shell: bash
run: |
"$PIP_PY" scripts/parity_check.py --label pip > fp-pip.json
- uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: craftbot
environment-file: environment.yml
auto-activate-base: false
# environment.yml deliberately lists no Python packages — they come from
# the shared lock. Without this step the conda env holds only the
# interpreter and system binaries, and the comparison below would report
# a 200-package "divergence" that is really just a missing install.
- name: Install the lock into the conda env
shell: bash -el {0}
run: |
LOCK=$(python -c "from app.provision.deps import find_lock; print(find_lock('.') or '')")
if [ -z "$LOCK" ]; then
echo "::error::no lock for this platform — run scripts/generate_lock.py"
exit 1
fi
echo "using $LOCK"
python -m pip install --require-hashes -r "$LOCK"
- name: Fingerprint conda install
shell: bash -el {0}
run: python scripts/parity_check.py --label conda > fp-conda.json
- name: Compare
shell: bash
run: python scripts/parity_check.py --compare fp-pip.json fp-conda.json
- name: Upload fingerprints
if: always()
uses: actions/upload-artifact@v4
with:
name: fingerprints-${{ matrix.os_label }}
path: fp-*.json
# ──────────────────────────────────────────────
# The installer path must produce the same thing
# as a source checkout. This is the acceptance
# test for the whole architecture, so it runs on
# every platform we ship.
# ──────────────────────────────────────────────
installer-e2e:
name: Installer E2E (${{ matrix.os_label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
os_label: linux
- os: windows-latest
os_label: windows
- os: macos-latest
os_label: macos
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Build frontend
shell: bash
env:
VITE_BACKEND_PORT: "7926"
run: |
cd app/ui_layer/browser/frontend
npm install
npx vite build
- name: Build the source payload
shell: bash
run: |
python scripts/package_source.py
# Structural only: verifies path resolution, the managed-install marker
# and payload completeness. The full run installs 239 packages and is
# too slow for every PR — the `parity` job covers dependency equality.
- name: Install E2E (structural)
shell: bash
run: |
python scripts/test_install_e2e.py --skip-deps
- name: Upload payload
if: always()
uses: actions/upload-artifact@v4
with:
name: payload-${{ matrix.os_label }}
path: dist/CraftBot-src.zip