-
Notifications
You must be signed in to change notification settings - Fork 0
248 lines (211 loc) · 8.31 KB
/
Copy pathci.yml
File metadata and controls
248 lines (211 loc) · 8.31 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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.12"
jobs:
lint:
name: Format and lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install ruff
run: pipx install ruff==0.16.3
- name: Check formatting
run: ruff format --check .
- name: Lint
run: ruff check --output-format=github .
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
- name: Install the formatter
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Check that every JSON and YAML file is formatted
run: pnpm run format:check
# The workflows are the one thing here nothing else checks, and a mistake
# in them is only visible once a run has already gone wrong. The archive
# is verified by digest before anything in it runs, for the same reason
# every document this project reads is.
- name: Check the workflows themselves
run: |
curl -fsSL -o actionlint.tar.gz \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c -
tar -xzf actionlint.tar.gz actionlint
./actionlint -color
rm -f actionlint actionlint.tar.gz
shell: bash
env:
ACTIONLINT_VERSION: "1.7.12"
ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8"
- name: Lint the shell scripts
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
shellcheck --severity=style --shell=bash scripts/*.sh
types:
name: Types
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install the type checker
run: pipx install mypy==1.14.1
# Strict, with every optional error class the version offers. A processor
# core is masked integers, flag bits and addressing modes end to end, and a
# width crossing a boundary it should not is exactly what this catches. The
# settings live in pyproject.toml so a local run and this one agree.
- name: Every annotation has to hold
run: mypy
test:
name: Tests on Python ${{ matrix.python }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python }}
- name: Install coverage
run: python -m pip install --disable-pip-version-check coverage==7.6.10
- name: Run every test file, under coverage
run: |
python -m coverage erase
status=0
while IFS= read -r file; do
echo "::group::${file}"
python -m coverage run -a "${file}" || status=1
echo "::endgroup::"
done < <(find mos65xx conformance -name '*.test.py' | sort)
exit "${status}"
shell: bash
- name: Coverage must be total
run: python -m coverage report
# No document is redistributable, so none is here and this checks nothing
# on a runner. It says so rather than reporting a pass: the value is that
# somebody running it with the documents on disk gets a real answer, and
# that a run without them cannot be mistaken for one.
- name: Every quoted sentence, against the document it came from
run: python -m conformance.quotes
# Uninstrumented, and deliberately after the coverage run rather than
# inside it. A coverage tracer costs about ten times what the model does, so
# a throughput check under one measures the tracer.
- name: The model has not become several times slower
run: python -m conformance.speed
# Opt-in, and silent when nothing is there. A transistor level simulation
# of the die answers what no data sheet states and no recording carries,
# but it is somebody else's work under their own licence, so a copy belongs
# on the machine that runs it rather than in this repository or this job.
- name: The model against a simulation of the die, when one is present
run: python -m conformance.netlist
conformance:
name: Conformance against SingleStepTests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
# One key for every suite, taken from the file that pins them all, so the
# two jobs that need them share one entry rather than filling the cache
# with the same gigabytes twice.
- name: Cache the suites
id: cache
uses: actions/cache@v6
with:
path: ~/.cache/conformance-suites
key: suites-${{ hashFiles('conformance/suites.json') }}
- name: Fetch the suite
if: steps.cache.outputs.cache-hit != 'true'
run: python -m conformance.fetch ~/.cache/conformance-suites
# A pull request runs a tenth of each suite for a quick answer. A push to
# main runs every case, because the number of them is what the claim of
# conformance rests on, and the claim covers every part rather than one.
- name: Run every suite
run: |
if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
limit=1000
else
limit=0
fi
status=0
while read -r suite model; do
echo "::group::${suite} as a ${model}"
python -m conformance.singlestep \
"${HOME}/.cache/conformance-suites/${suite}" "${limit}" --model "${model}" || status=1
echo "::endgroup::"
done <<'SUITES'
65816/65816/v1 65816
6502/6502/v1 6502
nes6502/nes6502/v1 2a03
synertek65c02/synertek65c02/v1 65c02
rockwell65c02/rockwell65c02/v1 r65c02
wdc65c02/wdc65c02/v1 w65c02
SUITES
exit "${status}"
shell: bash
cycles:
name: Cycle for cycle against SingleStepTests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}
# One key for every suite, taken from the file that pins them all, so the
# two jobs that need them share one entry rather than filling the cache
# with the same gigabytes twice.
- name: Cache the suites
id: cache
uses: actions/cache@v6
with:
path: ~/.cache/conformance-suites
key: suites-${{ hashFiles('conformance/suites.json') }}
- name: Fetch the suite
if: steps.cache.outputs.cache-hit != 'true'
run: python -m conformance.fetch ~/.cache/conformance-suites
# Every cycle of an eight-bit part is a bus cycle, so this compares timing
# and side effects at once: address, value, and read against write, in
# order. On the 65816 it also compares eight output pins per cycle, and the
# cycles where the part drives no valid address at all.
- name: Compare the bus, cycle by cycle
run: |
if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
limit=1000
else
limit=0
fi
status=0
while read -r suite model; do
echo "::group::${suite} as a ${model}"
python -m conformance.cycles \
"${HOME}/.cache/conformance-suites/${suite}" "${limit}" --model "${model}" || status=1
echo "::endgroup::"
done <<'SUITES'
65816/65816/v1 65816
6502/6502/v1 6502
nes6502/nes6502/v1 2a03
synertek65c02/synertek65c02/v1 65c02
rockwell65c02/rockwell65c02/v1 r65c02
wdc65c02/wdc65c02/v1 w65c02
SUITES
exit "${status}"
shell: bash