pl-e2e-test 0.1.8__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/MANIFEST.in +1 -0
- {pl_e2e_test-0.1.8/src/pl_e2e_test.egg-info → pl_e2e_test-0.2.1}/PKG-INFO +139 -69
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/README.md +136 -68
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/pyproject.toml +3 -1
- pl_e2e_test-0.2.1/scripts/demo_cli.py +219 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1/src/pl_e2e_test.egg-info}/PKG-INFO +139 -69
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/pl_e2e_test.egg-info/SOURCES.txt +9 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/pl_e2e_test.egg-info/requires.txt +2 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/__init__.py +50 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/_git.py +47 -3
- pl_e2e_test-0.2.1/src/prairielearn_e2e/_options.py +29 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/cli.py +721 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/config.py +150 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/fuzz.py +462 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/instances.py +199 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/parallel.py +350 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/pytest_plugin.py +91 -2
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/regression.py +22 -31
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/reporting.py +401 -35
- pl_e2e_test-0.2.1/src/prairielearn_e2e/repository.py +668 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/runner.py +82 -0
- pl_e2e_test-0.2.1/src/prairielearn_e2e/runtime.py +410 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/sharding.py +4 -5
- pl_e2e_test-0.2.1/src/prairielearn_e2e/ui.py +158 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/stub_git.py +33 -7
- pl_e2e_test-0.2.1/tests/test_cli.py +294 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_config.py +45 -1
- pl_e2e_test-0.2.1/tests/test_demo_cli.py +51 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_fuzz.py +253 -26
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_git.py +25 -1
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_harness.py +34 -1
- pl_e2e_test-0.2.1/tests/test_instances.py +303 -0
- pl_e2e_test-0.2.1/tests/test_options.py +17 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_parallel.py +221 -18
- pl_e2e_test-0.2.1/tests/test_publish_version.py +479 -0
- pl_e2e_test-0.2.1/tests/test_pytest_plugin.py +558 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_regression.py +1 -2
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_reporting.py +60 -2
- pl_e2e_test-0.2.1/tests/test_repository.py +678 -0
- pl_e2e_test-0.2.1/tests/test_runtime.py +498 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_sharding.py +1 -2
- pl_e2e_test-0.1.8/src/prairielearn_e2e/__init__.py +0 -23
- pl_e2e_test-0.1.8/src/prairielearn_e2e/cli.py +0 -58
- pl_e2e_test-0.1.8/src/prairielearn_e2e/config.py +0 -68
- pl_e2e_test-0.1.8/src/prairielearn_e2e/fuzz.py +0 -315
- pl_e2e_test-0.1.8/src/prairielearn_e2e/parallel.py +0 -250
- pl_e2e_test-0.1.8/src/prairielearn_e2e/repository.py +0 -298
- pl_e2e_test-0.1.8/src/prairielearn_e2e/runner.py +0 -28
- pl_e2e_test-0.1.8/tests/test_cli.py +0 -81
- pl_e2e_test-0.1.8/tests/test_publish_version.py +0 -169
- pl_e2e_test-0.1.8/tests/test_pytest_plugin.py +0 -202
- pl_e2e_test-0.1.8/tests/test_repository.py +0 -334
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/LICENSE +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/setup.cfg +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/pl_e2e_test.egg-info/dependency_links.txt +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/pl_e2e_test.egg-info/entry_points.txt +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/pl_e2e_test.egg-info/top_level.txt +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/__main__.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/backend.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/contract.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/discovery.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/errors.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/expected_errors.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/harness.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/html_checks.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/metadata.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/question_tests.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/src/prairielearn_e2e/server.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/__init__.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/conftest.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_contract.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_discovery.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_html_checks.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_metadata.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_question_tests.py +0 -0
- {pl_e2e_test-0.1.8 → pl_e2e_test-0.2.1}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pl-e2e-test
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Fuzz PrairieLearn question lifecycles against the real Python element runtime
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/PrairieLearn/pl-e2e-test
|
|
@@ -10,6 +10,7 @@ Requires-Python: >=3.13
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: chevron==0.14.0
|
|
13
|
+
Requires-Dist: cyclopts<6,>=5.3
|
|
13
14
|
Requires-Dist: filelock>=3.20
|
|
14
15
|
Requires-Dist: jsonschema>=4.26
|
|
15
16
|
Requires-Dist: lxml>=6.1
|
|
@@ -20,6 +21,7 @@ Requires-Dist: pyright>=1.1.414
|
|
|
20
21
|
Requires-Dist: pytest>=9.1
|
|
21
22
|
Requires-Dist: rich>=14
|
|
22
23
|
Requires-Dist: ruff>=0.16
|
|
24
|
+
Requires-Dist: tomlkit>=0.15.2
|
|
23
25
|
Dynamic: license-file
|
|
24
26
|
|
|
25
27
|
# pl-e2e-test
|
|
@@ -32,19 +34,24 @@ instead of copying the harness and its support scripts.
|
|
|
32
34
|
The package installs the reusable `prairielearn_e2e` Python API and a single
|
|
33
35
|
`pl-e2e-test` command:
|
|
34
36
|
|
|
35
|
-
- `pl-e2e-test`
|
|
36
|
-
lifecycle pytest suite.
|
|
37
|
+
- `pl-e2e-test run` runs the installed lifecycle pytest suite.
|
|
37
38
|
- `pl-e2e-test diff` fuzzes questions selected from a Git diff.
|
|
38
|
-
- `pl-e2e-test parallel`
|
|
39
|
+
- `pl-e2e-test parallel run` and `parallel diff` run local shards and merge them.
|
|
39
40
|
- `pl-e2e-test add-seed` records a failing concrete variant seed.
|
|
40
41
|
- `pl-e2e-test reports` merges parallel shard results into terminal and JUnit reports.
|
|
41
|
-
- `pl-e2e-test
|
|
42
|
-
|
|
42
|
+
- `pl-e2e-test instances` shows configured, installed, cached, and local PrairieLearn
|
|
43
|
+
revisions.
|
|
44
|
+
- `pl-e2e-test version [REVISION]` reads or updates the configured PrairieLearn revision.
|
|
45
|
+
- `pl-e2e-test cache clean` clears the managed PrairieLearn instance cache.
|
|
46
|
+
- `pl-e2e-test --version` prints the installed CLI version.
|
|
47
|
+
- `pl-e2e-test help [COMMAND...]` prints top-level or nested command help. A bare
|
|
48
|
+
invocation also prints top-level help.
|
|
43
49
|
|
|
44
50
|
## Requirements and installation
|
|
45
51
|
|
|
46
52
|
- Python 3.13 or newer
|
|
47
53
|
- Git 2.30 or newer
|
|
54
|
+
- uv 0.9 or newer
|
|
48
55
|
|
|
49
56
|
The harness depends on pytest, Pyright, Ruff, Chevron, JSON Schema, lxml, NumPy,
|
|
50
57
|
and PrairieLearn. PrairieLearn's Python package is currently consumed from its Git
|
|
@@ -80,20 +87,37 @@ Pass a question, a directory of questions, or an `info.json` file. Repeat
|
|
|
80
87
|
`--question-path` to select multiple roots:
|
|
81
88
|
|
|
82
89
|
```sh
|
|
83
|
-
uv run pl-e2e-test \
|
|
90
|
+
uv run pl-e2e-test run \
|
|
84
91
|
--question-path questions \
|
|
85
92
|
--seed-count 3
|
|
86
93
|
```
|
|
87
94
|
|
|
88
95
|
The installed suite exposes separate `generate`, `prepare`, `render`, and `grade`
|
|
89
|
-
pytest markers.
|
|
96
|
+
pytest markers. Put pytest selection and execution arguments after `--`:
|
|
90
97
|
|
|
91
98
|
```sh
|
|
92
|
-
uv run pl-e2e-test \
|
|
99
|
+
uv run pl-e2e-test -vv run \
|
|
93
100
|
--question-path questions/chapter/example \
|
|
94
|
-
--fuzz-seeds -m render -x
|
|
101
|
+
--fuzz-seeds -- -m render -x
|
|
95
102
|
```
|
|
96
103
|
|
|
104
|
+
The native reporter owns progress, capture, tracebacks, summaries, and color. Use
|
|
105
|
+
repeatable global `-q` or `-v` flags and `--color auto|always|never`; pytest
|
|
106
|
+
presentation flags such as `-q`, `-v`, `--tb`, `-r`, `--color`, and `-s` are rejected
|
|
107
|
+
after `--`. `NO_COLOR`, `FORCE_COLOR`, and `CLICOLOR_FORCE` are also honored.
|
|
108
|
+
|
|
109
|
+
To preview the terminal UX without a PrairieLearn course or network access, run:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
uv run python scripts/demo_cli.py
|
|
113
|
+
uv run python scripts/demo_cli.py --color always mixed verbose cli-error
|
|
114
|
+
uv run python scripts/demo_cli.py --list
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The demo covers help and CLI errors plus passing, failing, skipped, xfailed/xpassed,
|
|
118
|
+
warning, collection-error, empty-selection, quiet, verbose, and per-shard progress
|
|
119
|
+
output.
|
|
120
|
+
|
|
97
121
|
`--fuzz-seeds` creates and reports a random 64-bit master seed. Reproduce the same
|
|
98
122
|
variant set with `--fuzz-seed N`. Without either option, deterministic seeds start at
|
|
99
123
|
zero. Question metadata with `singleVariant: true` always uses seed zero.
|
|
@@ -103,22 +127,23 @@ zero. Question metadata with `singleVariant: true` always uses seed zero.
|
|
|
103
127
|
Run multiple shards in one machine or CI job, with six concurrent processes by default:
|
|
104
128
|
|
|
105
129
|
```sh
|
|
106
|
-
uv run pl-e2e-test parallel --shard-count 6
|
|
130
|
+
uv run pl-e2e-test parallel run --shard-count 6 \
|
|
107
131
|
--question-path questions --fuzz-seeds
|
|
108
132
|
|
|
109
|
-
uv run pl-e2e-test parallel --shard-count 6 --junitxml results.xml
|
|
133
|
+
uv run pl-e2e-test parallel diff --shard-count 6 --junitxml results.xml \
|
|
110
134
|
--base main --fuzz-seed 42
|
|
111
135
|
```
|
|
112
136
|
|
|
113
|
-
|
|
114
|
-
|
|
137
|
+
Native options go after `parallel run` or `parallel diff` and before an optional `--`;
|
|
138
|
+
only pytest arguments follow that boundary. Normal runs remain deterministic. For
|
|
115
139
|
`--fuzz-seeds` and diff runs, the launcher generates one shared master seed unless
|
|
116
140
|
`--fuzz-seed` is supplied, and prints a command for replaying the run without sharding.
|
|
117
141
|
|
|
118
142
|
Each shard runs in a separate Python process using the current environment and
|
|
119
|
-
working directory. Terminals show
|
|
120
|
-
lines. All shards finish before reports are merged, even when a test
|
|
121
|
-
|
|
143
|
+
working directory. Terminals show aggregate progress; redirected output uses stable
|
|
144
|
+
start and summary lines. All shards finish before reports are merged, even when a test
|
|
145
|
+
fails. Raw child output is shown only when a launch or internal failure cannot produce
|
|
146
|
+
a usable report.
|
|
122
147
|
Interrupting the launcher stops and reaps its children.
|
|
123
148
|
|
|
124
149
|
Intermediate reports are temporary. Pass the launcher's `--junitxml` option to retain
|
|
@@ -145,19 +170,19 @@ Relative JUnit paths are resolved against the course root. With `junitxml=None`,
|
|
|
145
170
|
merged output is temporary. Invalid launcher settings raise `ValueError`.
|
|
146
171
|
|
|
147
172
|
Courses using `scripts/run_e2e_shards.py` can replace its invocation with
|
|
148
|
-
`pl-e2e-test parallel`, move `--fuzz-seed` into the
|
|
173
|
+
`pl-e2e-test parallel run`, move `--fuzz-seed` into the native arguments, and explicitly
|
|
149
174
|
pass `--fuzz-seeds` for full-suite runs to preserve the script's fuzzing behavior.
|
|
150
175
|
For example:
|
|
151
176
|
|
|
152
177
|
```make
|
|
153
178
|
test-e2e-sharded:
|
|
154
|
-
uv run --active pl-e2e-test parallel --shard-count "$(E2E_SHARD_COUNT)"
|
|
155
|
-
$(E2E_TEST_ARGS) $(E2E_PRAIRIELEARN_ARG) --fuzz-seeds $(E2E_FUZZ_SEED_ARG) $(PYTEST_ARGS)
|
|
179
|
+
uv run --active pl-e2e-test parallel run --shard-count "$(E2E_SHARD_COUNT)" \
|
|
180
|
+
$(E2E_TEST_ARGS) $(E2E_PRAIRIELEARN_ARG) --fuzz-seeds $(E2E_FUZZ_SEED_ARG) -- $(PYTEST_ARGS)
|
|
156
181
|
|
|
157
182
|
test-e2e-fuzz-sharded:
|
|
158
|
-
uv run --active pl-e2e-test parallel --shard-count "$(E2E_SHARD_COUNT)"
|
|
183
|
+
uv run --active pl-e2e-test parallel diff --shard-count "$(E2E_SHARD_COUNT)" \
|
|
159
184
|
--base "$(E2E_DIFF_BASE)" --head "$(E2E_DIFF_HEAD)" \
|
|
160
|
-
--seed-count "$(E2E_FUZZ_SEED_COUNT)" $(E2E_FUZZ_SEED_ARG) $(E2E_PRAIRIELEARN_ARG) $(PYTEST_ARGS)
|
|
185
|
+
--seed-count "$(E2E_FUZZ_SEED_COUNT)" $(E2E_FUZZ_SEED_ARG) $(E2E_PRAIRIELEARN_ARG) -- $(PYTEST_ARGS)
|
|
161
186
|
```
|
|
162
187
|
|
|
163
188
|
### CI sharding
|
|
@@ -199,7 +224,7 @@ jobs:
|
|
|
199
224
|
- run: uv python install && uv sync --locked
|
|
200
225
|
- name: Run shard
|
|
201
226
|
run: |
|
|
202
|
-
uv run pl-e2e-test \
|
|
227
|
+
uv run pl-e2e-test run \
|
|
203
228
|
--question-path questions \
|
|
204
229
|
--seed-count 10 \
|
|
205
230
|
--fuzz-seed "${{ github.run_id }}" \
|
|
@@ -241,19 +266,62 @@ jobs:
|
|
|
241
266
|
```
|
|
242
267
|
|
|
243
268
|
The course must provide `.prairielearn/schemas/infoQuestion.json`. The harness checks
|
|
244
|
-
question metadata against that schema and
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
269
|
+
question metadata against that schema and resolves the configured PrairieLearn version
|
|
270
|
+
before each run. The default, `latest`, resolves the Git source's remote `HEAD`; a full
|
|
271
|
+
commit, branch, or unambiguous tag can be selected instead. Moving selectors require
|
|
272
|
+
network access and never silently fall back to stale cache state.
|
|
273
|
+
|
|
274
|
+
The selected revision's Python package and core elements are always used together. The
|
|
275
|
+
harness keeps a blob-filtered bare repository in the platform-standard user cache and
|
|
276
|
+
hydrates only `apps/prairielearn/python` and `apps/prairielearn/elements`. It launches
|
|
277
|
+
the selected Python package through a frozen uv overlay, without changing the course's
|
|
278
|
+
`pyproject.toml`, `uv.lock`, or virtual environment. A previously hydrated exact commit
|
|
279
|
+
can be reused offline when its uv dependencies are also cached.
|
|
249
280
|
|
|
250
281
|
To use an existing PrairieLearn worktree or bare repository instead, pass
|
|
251
282
|
`--prairielearn-path /path/to/PrairieLearn`. The repository must already contain the
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
283
|
+
selected revision. An explicit path is authoritative: the harness resolves selectors
|
|
284
|
+
against its local refs, does not fetch into it, and does not fall back to the managed
|
|
285
|
+
cache. For a local repository, `latest` means its current `HEAD`.
|
|
286
|
+
|
|
287
|
+
Inspect available revisions without contacting remotes:
|
|
288
|
+
|
|
289
|
+
```sh
|
|
290
|
+
uv run pl-e2e-test instances
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Add `--refresh` to resolve the configured moving selector and verify remote access.
|
|
294
|
+
The table distinguishes fully hydrated runtime revisions from legacy element-only cache
|
|
295
|
+
entries. Standard cache environment settings such as `XDG_CACHE_HOME` can relocate the
|
|
296
|
+
managed cache; CI systems can persist that directory between runs if desired.
|
|
297
|
+
Cache use refreshes each repository or hydrated runtime's last-use timestamp. Whenever
|
|
298
|
+
the managed cache is used, unlocked items that have been idle for more than 30 days are
|
|
299
|
+
removed automatically across all configured PrairieLearn sources.
|
|
300
|
+
|
|
301
|
+
Read the effective configured selector without resolving it or contacting the network:
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
uv run pl-e2e-test version
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Set `tool.pl-e2e-test.prairielearnVersion` in an existing `pyproject.toml` to a full
|
|
308
|
+
commit or verified PrairieLearn release tag with:
|
|
309
|
+
|
|
310
|
+
```sh
|
|
311
|
+
uv run pl-e2e-test version 3.2.0
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Release tags are verified against the configured PrairieLearn Git source before the
|
|
315
|
+
file is changed. Use `--config PATH` to update another existing TOML file. Comments and
|
|
316
|
+
unrelated formatting are preserved, and a missing file is not created.
|
|
317
|
+
|
|
318
|
+
Remove all managed bare repositories and hydrated runtime revisions with:
|
|
319
|
+
|
|
320
|
+
```sh
|
|
321
|
+
uv run pl-e2e-test cache clean
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The next test run fetches and hydrates its selected PrairieLearn revision again.
|
|
257
325
|
|
|
258
326
|
### Harness configuration
|
|
259
327
|
|
|
@@ -261,19 +329,25 @@ Selector settings are read automatically from the course's `pyproject.toml`:
|
|
|
261
329
|
|
|
262
330
|
```toml
|
|
263
331
|
[tool.pl-e2e-test]
|
|
332
|
+
prairielearnVersion = "latest"
|
|
264
333
|
elementSolutionSelectors = ["pl-sketch"]
|
|
265
334
|
nonGradableSelectors = ['pl-sketch[read-only="true"]', 'pl-sketch[read-only="1"]']
|
|
266
335
|
```
|
|
267
336
|
|
|
268
|
-
These are the defaults. `
|
|
337
|
+
These are the defaults. Omitting `prairielearnVersion` is equivalent to `"latest"`.
|
|
338
|
+
Use a full commit for reproducible or offline runs, or a branch/tag when a named moving
|
|
339
|
+
target is intentional. `--prairielearn-version SELECTOR` overrides the TOML value for
|
|
340
|
+
one invocation, and replay commands always print the resolved full commit.
|
|
341
|
+
|
|
342
|
+
`elementSolutionSelectors` identifies elements that provide
|
|
269
343
|
solutions without a `correct_answers` entry. It only relaxes that entry requirement;
|
|
270
344
|
it does not generate a test submission. `nonGradableSelectors` excludes display-only
|
|
271
345
|
answers from grading checks. Each supplied list replaces its default; omitted settings
|
|
272
346
|
keep their defaults, and `[]` disables a list.
|
|
273
347
|
|
|
274
348
|
Use `--config path/to/settings.toml` to select another TOML file with the same
|
|
275
|
-
`[tool.pl-e2e-test]` table. This works with
|
|
276
|
-
`parallel
|
|
349
|
+
`[tool.pl-e2e-test]` table. This works with `run`, `diff`, `parallel run`, and
|
|
350
|
+
`parallel diff`. Explicit paths are relative to the working directory.
|
|
277
351
|
|
|
278
352
|
### Lifecycle JSON boundary
|
|
279
353
|
|
|
@@ -427,7 +501,7 @@ diffignore = [
|
|
|
427
501
|
|
|
428
502
|
Rules use Git's native ignore syntax and are evaluated in order, so later rules may
|
|
429
503
|
negate earlier ones with `!`. A missing `pyproject.toml`, tool table, or `diffignore`
|
|
430
|
-
entry means that no paths are ignored. Additional arguments
|
|
504
|
+
entry means that no paths are ignored. Additional pytest arguments must follow `--`.
|
|
431
505
|
|
|
432
506
|
To migrate from `.question-e2e.diffignore`, copy its non-comment rules into the
|
|
433
507
|
`diffignore` array. The former file and `--e2ediffignore` option are no longer used.
|
|
@@ -455,12 +529,16 @@ course's TOML settings, or `config=load_config("settings.toml")` for an explicit
|
|
|
455
529
|
`load_config` and `HarnessConfig` are exported from `prairielearn_e2e`. Omitting
|
|
456
530
|
`config` uses the built-in defaults.
|
|
457
531
|
|
|
532
|
+
The command-line runner and high-level `run_parallel` API perform version resolution
|
|
533
|
+
and uv bootstrapping. Manually constructed `PrairieLearnBackend` and `QuestionHarness`
|
|
534
|
+
objects continue to use the PrairieLearn package already loaded by their interpreter.
|
|
535
|
+
|
|
458
536
|
## Development
|
|
459
537
|
|
|
460
538
|
This project uses uv 0.9 or newer:
|
|
461
539
|
|
|
462
540
|
```sh
|
|
463
|
-
|
|
541
|
+
make install
|
|
464
542
|
make test
|
|
465
543
|
make format
|
|
466
544
|
make smoke-dist
|
|
@@ -472,44 +550,36 @@ into an isolated target, and imports it using the locked development dependencie
|
|
|
472
550
|
|
|
473
551
|
## Publishing
|
|
474
552
|
|
|
475
|
-
There are three supported publishing paths. Choose one for a release; do not run the
|
|
476
|
-
local and GitHub paths for the same version because PyPI versions are immutable.
|
|
477
|
-
|
|
478
|
-
### GitHub-hosted trusted publishing
|
|
479
|
-
|
|
480
553
|
Configure the PyPI project to trust the `publish.yml` workflow in the GitHub `pypi`
|
|
481
|
-
environment.
|
|
554
|
+
environment. Releases must start from a clean `main` that is synchronized with
|
|
555
|
+
`origin/main`, with `gh` authenticated and the publishing workflow available. To bump
|
|
556
|
+
the current version, run one of:
|
|
482
557
|
|
|
483
558
|
```sh
|
|
484
|
-
make publish
|
|
559
|
+
make publish patch
|
|
560
|
+
make publish minor
|
|
561
|
+
make publish major
|
|
485
562
|
```
|
|
486
563
|
|
|
487
|
-
|
|
488
|
-
the full checks, updates `pyproject.toml` and `uv.lock`, builds and smoke-tests the
|
|
489
|
-
distributions, creates a release commit and annotated `v0.2.0` tag, and atomically
|
|
490
|
-
pushes the branch and tag. The tag starts the workflow on `ubuntu-latest`; the workflow
|
|
491
|
-
rechecks the tag/version match, tests, builds, generates attestations, and publishes
|
|
492
|
-
with PyPI trusted-publishing OIDC credentials. Use `REMOTE=name` for a different Git
|
|
493
|
-
remote.
|
|
494
|
-
|
|
495
|
-
If an unpushed commit already contains the requested version, use the guarded bypass:
|
|
564
|
+
If the synchronized commit already contains the intended, not-yet-tagged version, use:
|
|
496
565
|
|
|
497
566
|
```sh
|
|
498
|
-
make publish
|
|
567
|
+
make publish current
|
|
499
568
|
```
|
|
500
569
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
570
|
+
The release command runs the shared `make release-check` gate, updates
|
|
571
|
+
`pyproject.toml` and `uv.lock` when needed, creates a release commit and annotated tag,
|
|
572
|
+
atomically pushes `main` and the tag, and watches the trusted-publishing workflow to
|
|
573
|
+
completion. Preview the full process without changing files, refs, remotes, or workflows
|
|
574
|
+
with:
|
|
505
575
|
|
|
506
576
|
```sh
|
|
507
|
-
make publish
|
|
577
|
+
make publish patch DRY_RUN=1
|
|
508
578
|
```
|
|
509
579
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
580
|
+
If the release tag exists but trusted publishing needs to be dispatched again, run
|
|
581
|
+
`make publish-pypi`. This validates that the tag matches the current version and is
|
|
582
|
+
reachable from `origin/main`, then dispatches and watches a new workflow run.
|
|
513
583
|
|
|
514
584
|
### Local self-hosted GitHub runner
|
|
515
585
|
|
|
@@ -518,11 +588,11 @@ inside Docker. The container has no host mounts and does not receive the Docker
|
|
|
518
588
|
Authenticate `gh` with repository administration permission, then run:
|
|
519
589
|
|
|
520
590
|
```sh
|
|
521
|
-
make
|
|
522
|
-
make publish-self-hosted RELEASE_TAG=v0.2.0
|
|
591
|
+
make publish-self-hosted
|
|
523
592
|
```
|
|
524
593
|
|
|
525
|
-
The tag must already exist. This
|
|
526
|
-
`
|
|
527
|
-
|
|
528
|
-
|
|
594
|
+
The current version's tag must already exist. This ensures the local runner is online,
|
|
595
|
+
dispatches `publish.yml` from `main` with the `self-hosted` runner choice, and watches
|
|
596
|
+
the run. `make runner-ensure` starts Docker Desktop on macOS when needed and repairs the
|
|
597
|
+
runner before dispatch. Other lifecycle commands are `runner-status`, `runner-stop`,
|
|
598
|
+
`runner-remove`, `runner-logs`, and `clean-runner`.
|