revision-engine 0.2.2__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.
- revision_engine-0.2.2/.gitattributes +4 -0
- revision_engine-0.2.2/.github/dependabot.yml +10 -0
- revision_engine-0.2.2/.github/workflows/demo.yml +36 -0
- revision_engine-0.2.2/.github/workflows/test.yml +178 -0
- revision_engine-0.2.2/.gitignore +9 -0
- revision_engine-0.2.2/LICENSE +21 -0
- revision_engine-0.2.2/PKG-INFO +134 -0
- revision_engine-0.2.2/README.md +116 -0
- revision_engine-0.2.2/RELEASING.md +130 -0
- revision_engine-0.2.2/ci-constraints-requirements.txt +13 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/anchors.yaml +14 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/cues.json +34 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/diagrams/flow.dot +8 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/lesson.md +26 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/media/narration.wav +0 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/01-hello/narration.yaml +9 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/02-quiet/anchors.yaml +3 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/chapters/01-intro/lessons/02-quiet/lesson.md +7 -0
- revision_engine-0.2.2/examples/minimal/docs/revision/curriculum.yaml +10 -0
- revision_engine-0.2.2/examples/minimal/revision.yaml +10 -0
- revision_engine-0.2.2/examples/minimal/src/hello.c +15 -0
- revision_engine-0.2.2/pyproject.toml +35 -0
- revision_engine-0.2.2/revision_engine/__init__.py +3 -0
- revision_engine-0.2.2/revision_engine/anchors.py +163 -0
- revision_engine-0.2.2/revision_engine/build.py +268 -0
- revision_engine-0.2.2/revision_engine/cli.py +159 -0
- revision_engine-0.2.2/revision_engine/config.py +159 -0
- revision_engine-0.2.2/revision_engine/diagrams.py +108 -0
- revision_engine-0.2.2/revision_engine/errors.py +18 -0
- revision_engine-0.2.2/revision_engine/highlight.py +62 -0
- revision_engine-0.2.2/revision_engine/intrinsics/__init__.py +34 -0
- revision_engine-0.2.2/revision_engine/intrinsics/arm.yaml +22 -0
- revision_engine-0.2.2/revision_engine/intrinsics/intel.yaml +42 -0
- revision_engine-0.2.2/revision_engine/intrinsics/riscv.yaml +28 -0
- revision_engine-0.2.2/revision_engine/lesson.py +64 -0
- revision_engine-0.2.2/revision_engine/markdown_ext.py +285 -0
- revision_engine-0.2.2/revision_engine/narration.py +276 -0
- revision_engine-0.2.2/revision_engine/render.py +119 -0
- revision_engine-0.2.2/revision_engine/serve.py +153 -0
- revision_engine-0.2.2/revision_engine/sources.py +61 -0
- revision_engine-0.2.2/revision_engine/static/app.js +292 -0
- revision_engine-0.2.2/revision_engine/static/styles.css +160 -0
- revision_engine-0.2.2/revision_engine/templates/lesson.html.j2 +89 -0
- revision_engine-0.2.2/revision_engine/templates/redirect.html.j2 +5 -0
- revision_engine-0.2.2/tests/__init__.py +0 -0
- revision_engine-0.2.2/tests/browser/__init__.py +0 -0
- revision_engine-0.2.2/tests/browser/conftest.py +54 -0
- revision_engine-0.2.2/tests/browser/marks.py +89 -0
- revision_engine-0.2.2/tests/browser/test_diagram_links.py +140 -0
- revision_engine-0.2.2/tests/browser/test_layout.py +62 -0
- revision_engine-0.2.2/tests/browser/test_media_collapse.py +164 -0
- revision_engine-0.2.2/tests/browser/test_media_error.py +202 -0
- revision_engine-0.2.2/tests/browser/test_narrow_media.py +148 -0
- revision_engine-0.2.2/tests/browser/test_nested_intrinsic.py +149 -0
- revision_engine-0.2.2/tests/browser/test_pages.py +225 -0
- revision_engine-0.2.2/tests/browser/test_quiz_order.py +109 -0
- revision_engine-0.2.2/tests/ci_pins.py +97 -0
- revision_engine-0.2.2/tests/conftest.py +37 -0
- revision_engine-0.2.2/tests/golden/minimal.json +12 -0
- revision_engine-0.2.2/tests/golden_digest.py +86 -0
- revision_engine-0.2.2/tests/require_golden.py +57 -0
- revision_engine-0.2.2/tests/test_anchors.py +225 -0
- revision_engine-0.2.2/tests/test_browser_marks.py +158 -0
- revision_engine-0.2.2/tests/test_build.py +184 -0
- revision_engine-0.2.2/tests/test_ci_constraints.py +208 -0
- revision_engine-0.2.2/tests/test_cli.py +320 -0
- revision_engine-0.2.2/tests/test_config.py +81 -0
- revision_engine-0.2.2/tests/test_determinism.py +23 -0
- revision_engine-0.2.2/tests/test_diagrams.py +222 -0
- revision_engine-0.2.2/tests/test_errors.py +12 -0
- revision_engine-0.2.2/tests/test_golden.py +43 -0
- revision_engine-0.2.2/tests/test_golden_digest.py +89 -0
- revision_engine-0.2.2/tests/test_highlight.py +52 -0
- revision_engine-0.2.2/tests/test_intrinsics.py +95 -0
- revision_engine-0.2.2/tests/test_lesson.py +42 -0
- revision_engine-0.2.2/tests/test_markdown_ext.py +528 -0
- revision_engine-0.2.2/tests/test_narration.py +278 -0
- revision_engine-0.2.2/tests/test_release.py +634 -0
- revision_engine-0.2.2/tests/test_release_doc.py +135 -0
- revision_engine-0.2.2/tests/test_require_golden.py +80 -0
- revision_engine-0.2.2/tests/test_serve.py +89 -0
- revision_engine-0.2.2/tests/test_sources.py +54 -0
- revision_engine-0.2.2/tests/test_static.py +21 -0
- revision_engine-0.2.2/tests/tools/make_fixture_audio.py +18 -0
- revision_engine-0.2.2/tools/__init__.py +0 -0
- revision_engine-0.2.2/tools/release.py +324 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: demo-pages
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
pages: write
|
|
8
|
+
id-token: write
|
|
9
|
+
concurrency:
|
|
10
|
+
group: pages
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
- run: sudo apt-get update && sudo apt-get install -y graphviz
|
|
21
|
+
- run: python -m pip install -e .
|
|
22
|
+
# tests/test_ci_constraints.py::test_demo_workflow_checks_before_building pins this step before rv2 build.
|
|
23
|
+
- run: rv2 check --strict --root examples/minimal
|
|
24
|
+
- run: rv2 build --root examples/minimal --output public
|
|
25
|
+
- uses: actions/upload-pages-artifact@v3
|
|
26
|
+
with:
|
|
27
|
+
path: public
|
|
28
|
+
deploy:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment:
|
|
32
|
+
name: github-pages
|
|
33
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
34
|
+
steps:
|
|
35
|
+
- id: deployment
|
|
36
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
env:
|
|
12
|
+
RV_REQUIRE_GOLDEN: "1"
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- run: sudo apt-get update && sudo apt-get install -y graphviz
|
|
19
|
+
- run: python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt
|
|
20
|
+
- run: python -m playwright install --with-deps chromium
|
|
21
|
+
- run: python -m pytest -vv --browser chromium
|
|
22
|
+
browsers:
|
|
23
|
+
runs-on: ubuntu-24.04
|
|
24
|
+
strategy:
|
|
25
|
+
fail-fast: false
|
|
26
|
+
matrix:
|
|
27
|
+
browser: [firefox, webkit]
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
- run: sudo apt-get update && sudo apt-get install -y graphviz
|
|
34
|
+
- run: python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt
|
|
35
|
+
- run: python -m playwright install --with-deps ${{ matrix.browser }}
|
|
36
|
+
- run: python -m playwright --version
|
|
37
|
+
- run: python -m pytest -vv -rxXfE tests/browser --browser ${{ matrix.browser }}
|
|
38
|
+
# Tags and releases a merged version bump (RELEASING.md step 4). A job here, not a
|
|
39
|
+
# workflow_run workflow, so it sees the push's `before` and runs only after the `test` and
|
|
40
|
+
# `browsers` jobs pass; the upload jobs below run after it.
|
|
41
|
+
release:
|
|
42
|
+
needs: [test, browsers]
|
|
43
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'mtibbits/reVision'
|
|
44
|
+
runs-on: ubuntu-24.04
|
|
45
|
+
outputs:
|
|
46
|
+
version: ${{ steps.decide.outputs.version }}
|
|
47
|
+
permissions:
|
|
48
|
+
contents: write
|
|
49
|
+
pull-requests: read
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
with:
|
|
53
|
+
fetch-depth: 0
|
|
54
|
+
- uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
- id: decide
|
|
58
|
+
env:
|
|
59
|
+
BEFORE: ${{ github.event.before }}
|
|
60
|
+
run: python tools/release.py decide --before "$BEFORE" --after "$GITHUB_SHA"
|
|
61
|
+
- if: steps.decide.outputs.tag == 'true'
|
|
62
|
+
env:
|
|
63
|
+
VERSION: ${{ steps.decide.outputs.version }}
|
|
64
|
+
run: |
|
|
65
|
+
git -c user.name="github-actions[bot]" -c user.email="41898282+github-actions[bot]@users.noreply.github.com" tag -a "v$VERSION" -m "reVision engine v$VERSION" "$GITHUB_SHA"
|
|
66
|
+
git push origin "refs/tags/v$VERSION"
|
|
67
|
+
- id: exists
|
|
68
|
+
if: steps.decide.outputs.release == 'true'
|
|
69
|
+
env:
|
|
70
|
+
VERSION: ${{ steps.decide.outputs.version }}
|
|
71
|
+
GH_TOKEN: ${{ github.token }}
|
|
72
|
+
run: |
|
|
73
|
+
gh release list -R "$GITHUB_REPOSITORY" --limit 1000 --json tagName > "$RUNNER_TEMP/releases.json"
|
|
74
|
+
python tools/release.py exists --version "$VERSION" --releases "$RUNNER_TEMP/releases.json"
|
|
75
|
+
- if: steps.decide.outputs.release == 'true' && steps.exists.outputs.exists == 'false'
|
|
76
|
+
env:
|
|
77
|
+
VERSION: ${{ steps.decide.outputs.version }}
|
|
78
|
+
GH_TOKEN: ${{ github.token }}
|
|
79
|
+
run: |
|
|
80
|
+
gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/pulls" > "$RUNNER_TEMP/pulls.json"
|
|
81
|
+
python tools/release.py notes --version "$VERSION" --sha "$GITHUB_SHA" --pulls "$RUNNER_TEMP/pulls.json" > "$RUNNER_TEMP/notes.md"
|
|
82
|
+
gh release create "v$VERSION" -R "$GITHUB_REPOSITORY" --verify-tag --title "reVision engine v$VERSION" --notes-file "$RUNNER_TEMP/notes.md"
|
|
83
|
+
# Upload the release (RELEASING.md step 4). Jobs here, not a tag-triggered workflow: the
|
|
84
|
+
# release job's tag push uses GITHUB_TOKEN, which starts no workflow.
|
|
85
|
+
dist:
|
|
86
|
+
needs: release
|
|
87
|
+
if: needs.release.outputs.version != ''
|
|
88
|
+
runs-on: ubuntu-24.04
|
|
89
|
+
outputs:
|
|
90
|
+
wheel-sha256: ${{ steps.digests.outputs.wheel }}
|
|
91
|
+
sdist-sha256: ${{ steps.digests.outputs.sdist }}
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
with:
|
|
95
|
+
ref: refs/tags/v${{ needs.release.outputs.version }}
|
|
96
|
+
persist-credentials: false
|
|
97
|
+
- uses: actions/setup-python@v5
|
|
98
|
+
with:
|
|
99
|
+
python-version: "3.12"
|
|
100
|
+
- run: python -m pip install build -c ci-constraints-requirements.txt
|
|
101
|
+
- run: python -m build
|
|
102
|
+
- env:
|
|
103
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
104
|
+
run: python tools/release.py dist --version "$VERSION" --dir dist
|
|
105
|
+
- id: digests
|
|
106
|
+
env:
|
|
107
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
108
|
+
run: |
|
|
109
|
+
wheel=$(sha256sum "dist/revision_engine-$VERSION-py3-none-any.whl")
|
|
110
|
+
sdist=$(sha256sum "dist/revision_engine-$VERSION.tar.gz")
|
|
111
|
+
echo "wheel=${wheel%% *}" >> "$GITHUB_OUTPUT"
|
|
112
|
+
echo "sdist=${sdist%% *}" >> "$GITHUB_OUTPUT"
|
|
113
|
+
- uses: actions/upload-artifact@v4
|
|
114
|
+
with:
|
|
115
|
+
name: dist
|
|
116
|
+
path: dist/
|
|
117
|
+
if-no-files-found: error
|
|
118
|
+
overwrite: true
|
|
119
|
+
testpypi:
|
|
120
|
+
needs: dist
|
|
121
|
+
runs-on: ubuntu-24.04
|
|
122
|
+
environment: testpypi
|
|
123
|
+
permissions:
|
|
124
|
+
id-token: write
|
|
125
|
+
steps:
|
|
126
|
+
- uses: actions/download-artifact@v4
|
|
127
|
+
with:
|
|
128
|
+
name: dist
|
|
129
|
+
path: dist/
|
|
130
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
131
|
+
with:
|
|
132
|
+
repository-url: https://test.pypi.org/legacy/
|
|
133
|
+
# No token here: --extra-index-url lets TestPyPI, where anyone can upload anything, supply a
|
|
134
|
+
# dependency, so code from this install never runs where an id-token can be minted.
|
|
135
|
+
smoke:
|
|
136
|
+
needs: [release, testpypi]
|
|
137
|
+
runs-on: ubuntu-24.04
|
|
138
|
+
steps:
|
|
139
|
+
- uses: actions/checkout@v4
|
|
140
|
+
with:
|
|
141
|
+
ref: refs/tags/v${{ needs.release.outputs.version }}
|
|
142
|
+
persist-credentials: false
|
|
143
|
+
- uses: actions/setup-python@v5
|
|
144
|
+
with:
|
|
145
|
+
python-version: "3.12"
|
|
146
|
+
- env:
|
|
147
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
148
|
+
run: |
|
|
149
|
+
python -m venv "$RUNNER_TEMP/smoke"
|
|
150
|
+
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
|
151
|
+
"$RUNNER_TEMP/smoke/bin/python" -m pip install --no-cache-dir --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ -c ci-constraints-requirements.txt "revision-engine==$VERSION" && break
|
|
152
|
+
if [ "$attempt" = 10 ]; then exit 1; fi
|
|
153
|
+
sleep 30
|
|
154
|
+
done
|
|
155
|
+
cd "$RUNNER_TEMP"
|
|
156
|
+
out=$("$RUNNER_TEMP/smoke/bin/rv2" --version)
|
|
157
|
+
echo "$out"
|
|
158
|
+
test "$out" = "rv2 $VERSION"
|
|
159
|
+
# Uploads only the files dist hashed: code that smoke ran from TestPyPI must not choose them.
|
|
160
|
+
pypi:
|
|
161
|
+
needs: [release, dist, smoke]
|
|
162
|
+
runs-on: ubuntu-24.04
|
|
163
|
+
environment: pypi
|
|
164
|
+
permissions:
|
|
165
|
+
id-token: write
|
|
166
|
+
steps:
|
|
167
|
+
- uses: actions/download-artifact@v4
|
|
168
|
+
with:
|
|
169
|
+
name: dist
|
|
170
|
+
path: dist/
|
|
171
|
+
- env:
|
|
172
|
+
VERSION: ${{ needs.release.outputs.version }}
|
|
173
|
+
WHEEL_SHA256: ${{ needs.dist.outputs.wheel-sha256 }}
|
|
174
|
+
SDIST_SHA256: ${{ needs.dist.outputs.sdist-sha256 }}
|
|
175
|
+
run: |
|
|
176
|
+
test "$(ls -A dist | wc -l)" = 2
|
|
177
|
+
printf '%s %s\n' "$WHEEL_SHA256" "dist/revision_engine-$VERSION-py3-none-any.whl" "$SDIST_SHA256" "dist/revision_engine-$VERSION.tar.gz" | sha256sum --strict -c
|
|
178
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Tibbits
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: revision-engine
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: reVision: a static-site build tool for teaching code by example.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mtibbits/reVision
|
|
6
|
+
Author: Matthew Tibbits
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Requires-Dist: jinja2>=3.1
|
|
11
|
+
Requires-Dist: markdown-it-py>=3.0
|
|
12
|
+
Requires-Dist: pygments>=2.17
|
|
13
|
+
Requires-Dist: pyyaml>=6.0
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest-playwright>=0.5; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# reVision²
|
|
20
|
+
|
|
21
|
+
A static-site build tool for teaching code by example. A reader sees a real source
|
|
22
|
+
file on the left and a lesson about it on the right. Phrases in the lesson box the
|
|
23
|
+
code they describe. A narration track drives the same boxes on a timeline.
|
|
24
|
+
|
|
25
|
+
Install the engine as in [step 1](#add-revision-to-a-repository) below, add a `revision.yaml` and a
|
|
26
|
+
`docs/revision/` folder to the repository you want to teach, and run `rv2 build`.
|
|
27
|
+
Revision control, revisited.
|
|
28
|
+
|
|
29
|
+
## Add reVision to a repository
|
|
30
|
+
|
|
31
|
+
1. Install the engine: `pip install revision-engine`. To pin a release tag instead:
|
|
32
|
+
`pip install "revision-engine @ git+https://github.com/mtibbits/reVision@v0.2.2"`, which needs `git`.
|
|
33
|
+
Graphviz (`dot`) must be on the PATH for diagrams.
|
|
34
|
+
2. Create `revision.yaml` at the repository root:
|
|
35
|
+
```yaml
|
|
36
|
+
site: { title: My project by example, base_url: "" }
|
|
37
|
+
repo: { url: https://github.com/you/project, host: github } # or host: gitlab
|
|
38
|
+
content: docs/revision
|
|
39
|
+
output: public
|
|
40
|
+
```
|
|
41
|
+
3. Create `docs/revision/curriculum.yaml` and one lesson folder holding `lesson.md`
|
|
42
|
+
and `anchors.yaml`. Copy `examples/minimal/docs/revision/` from this repository to start.
|
|
43
|
+
4. `rv2 check` to validate, `rv2 serve` to preview, `rv2 build` to write `public/`.
|
|
44
|
+
`--root` must be the repository's git root; the code pane's "open at commit" link is built from it.
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
| Command | What it does |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `rv2 build` | Writes the site into the output folder. No network. Same commit, same bytes. |
|
|
51
|
+
| `rv2 check [--strict] [--list-unlinked]` | Runs every validation without writing. Use it as a pre-commit hook or CI gate. `--strict` exits 1 if any warning is reported; `--list-unlinked` lists each inline code span (not fenced blocks) that matches no vendor intrinsic table, per lesson, without changing the exit code. |
|
|
52
|
+
| `rv2 serve` | Builds to a temp folder, serves on localhost, rebuilds on change. |
|
|
53
|
+
| `rv2 narrate LESSON [--model voice.onnx]` | Synthesizes `narration.yaml` with Piper into audio plus `cues.json`. Commit both. |
|
|
54
|
+
|
|
55
|
+
For `narrate`, `pip install piper-tts` and download a voice (`.onnx` plus its `.onnx.json`) from
|
|
56
|
+
[rhasspy/piper-voices](https://huggingface.co/rhasspy/piper-voices); the example lesson uses
|
|
57
|
+
`en_US-lessac-medium`. Without `--model` the voice comes from `--voice`, then the lesson's `narration.yaml`
|
|
58
|
+
`voice`, then its front matter `voice`, then the project's `narration.voice`, and the model is `<voice>.onnx`
|
|
59
|
+
directly in `$RV2_VOICES` or `~/piper-voices`. A voice is a name, not a path: it may use only ASCII letters,
|
|
60
|
+
digits, `_`, `-` and `.`, not starting with `-` or `.`, and `rv2 narrate` refuses anything else. To use a model
|
|
61
|
+
file somewhere else, pass `--model`. `--silent` writes a silent placeholder track with plausible timings when
|
|
62
|
+
Piper is unavailable.
|
|
63
|
+
|
|
64
|
+
`revision` is an alias for `rv2`.
|
|
65
|
+
|
|
66
|
+
## Writing a lesson
|
|
67
|
+
|
|
68
|
+
- `lesson.md` starts with front matter: `title`, `file` (the source file shown), and optionally
|
|
69
|
+
`summary`, `start` (the anchor boxed on load), `media`, and `voice` (a voice name, not a path; see
|
|
70
|
+
`narrate`).
|
|
71
|
+
- `anchors.yaml` names code regions. Each anchor uses one form: `from`/`to` (a line containing
|
|
72
|
+
`from`, then the first later line matching the `to` regex), `match` (one line), or
|
|
73
|
+
`lines: 88-92`. Add `variant: true` and a `label` to list it in the Variants panel.
|
|
74
|
+
- `within: <anchor>` scopes `match`, `from` and `to` to another anchor's lines, for files where the
|
|
75
|
+
same body appears twice (aligned and unaligned variants).
|
|
76
|
+
- In the body, `[phrase](@anchor)` boxes a region, `[name](!intrinsic)` links a SIMD intrinsic to
|
|
77
|
+
its vendor documentation (Intel and Arm names to their intrinsic's page, RISC-V Vector names to
|
|
78
|
+
their section of the v1.0 intrinsic reference; a bare `` `name` `` in inline code auto-links
|
|
79
|
+
too, except inside another link's text, where it stays plain inline code; a code span in an
|
|
80
|
+
anchor phrase still links), `` inlines a Graphviz diagram (give a node
|
|
81
|
+
`id="anchor-name"` to make it hover and click like a phrase; a `URL`, `href` or HTML-label `HREF`
|
|
82
|
+
on such a node is removed with a warning, because a click on it pins the anchor), and a fenced
|
|
83
|
+
block with the language `quiz` adds a page-local knowledge check.
|
|
84
|
+
- In a quiz, a question's `q` and its `choices` accept one kind of markup: a backtick code span,
|
|
85
|
+
which renders as inline code (not linked to vendor documentation). Anything else, raw HTML and
|
|
86
|
+
backslashes included, shows as written, so a backslash does not stop a backtick from opening a
|
|
87
|
+
code span. YAML does not allow an unquoted value to begin with a backtick, so single-quote a `q`
|
|
88
|
+
or choice that starts with one (`` q: '`vaddq_f32` adds how many lanes?' ``); unquoted, the
|
|
89
|
+
build fails with "quiz block is not valid YAML". Use single quotes, not double: inside double
|
|
90
|
+
quotes YAML reads a backslash as an escape, so `` `\n` `` would lose its backslash. Inside single
|
|
91
|
+
quotes, write an apostrophe twice (`''`).
|
|
92
|
+
- A quiz block's only top-level key is `questions`. A question accepts `q`, `choices`, `answer`
|
|
93
|
+
(the index of the right choice, counting from 0) and `shuffle`; any other key fails the build
|
|
94
|
+
and is named in the error, so a misspelled `shufle: false` cannot leave a question shuffled.
|
|
95
|
+
- Each time the page loads, a question's choices appear in a random order, so the right answer is
|
|
96
|
+
not always in the same place; grading follows the choice, not its position. Add `shuffle: false`
|
|
97
|
+
to a question to keep its choices in the order written, for example numeric ranges or "all of
|
|
98
|
+
the above". A question with two choices (True/False) always keeps its order, so `shuffle: true`
|
|
99
|
+
on one is a build warning. With JavaScript off, choices appear in the order written.
|
|
100
|
+
- `narration.yaml` is a list of segments: `text` plus what to `show`, and optionally a `diagram`
|
|
101
|
+
and `node` to bring into view. Timings are derived by `rv2 narrate`, never typed. If the words
|
|
102
|
+
change and `narrate` is not rerun, `build` fails with the diff.
|
|
103
|
+
|
|
104
|
+
## Deploy
|
|
105
|
+
|
|
106
|
+
<!-- tests/test_release_doc.py::test_deploy_runs_the_strict_check_before_building checks that each platform's recipe below runs the strict check before building. -->
|
|
107
|
+
GitHub Actions: install Python and Graphviz, install the engine as in
|
|
108
|
+
[step 1](#add-revision-to-a-repository), run `rv2 check --strict` so any warning fails the job before anything is published, then
|
|
109
|
+
`rv2 build`, and upload `public/` with `actions/upload-pages-artifact`. GitLab CI: install Python,
|
|
110
|
+
Graphviz and the engine the same way and run `rv2 check --strict`, then `rv2 build`, in a `pages`
|
|
111
|
+
job with `public/` as its artifact, on an image that has `git`.
|
|
112
|
+
`.github/workflows/demo.yml` in this repository builds `examples/minimal/` that way.
|
|
113
|
+
|
|
114
|
+
## Developing the engine
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python -m venv .venv && source .venv/Scripts/activate # or .venv/bin/activate
|
|
118
|
+
python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt && python -m playwright install chromium
|
|
119
|
+
python -m pytest --browser chromium
|
|
120
|
+
python -m playwright install firefox webkit
|
|
121
|
+
python -m pytest tests/browser --browser firefox --browser webkit
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The golden test compares the built example site byte-for-byte, except that each diagram is reduced to its nodes (ids, anchor stamps, labels) and edges (endpoints, labels), so Graphviz layout and fonts do not matter.
|
|
125
|
+
That reduction was verified identical on Graphviz 16.1.0 (Windows) and on Ubuntu 22.04's `graphviz` 2.42.2 package (which reports itself as 2.43.0); a Graphviz that changes node ids, anchor stamps, edges, or node and edge labels fails the test (graph and cluster labels and link targets are not compared).
|
|
126
|
+
CI runs on the pinned `ubuntu-24.04` image and installs its `graphviz` package (2.42.2). Its `test` job sets `RV_REQUIRE_GOLDEN=1` and runs the whole suite in Chromium, so a skipped or missing golden test fails the job; the gate's message is the last line of the log. A separate `browsers` job runs `tests/browser` in Firefox and WebKit without the golden gate; a test that cannot pass in one engine carries `@pytest.mark.xfail_browser(...)` naming its tracking issue, and is a strict xfail there. On a push to `main` that bumps the version, a `release` job tags and releases it after both pass, and later jobs upload it to TestPyPI, install it from there, and upload it to PyPI; see RELEASING.md step 4.
|
|
127
|
+
<!-- tests/test_ci_constraints.py checks that every README line installing .[dev], here and in the fence above, contains INSTALL_CMD from tests/ci_pins.py. -->
|
|
128
|
+
CI installs the runtime dependencies whose output the golden test hashes (Pygments, markdown-it-py, Jinja2, PyYAML and their dependencies) at the versions pinned in `ci-constraints-requirements.txt`; `pyproject.toml` keeps floors only, so installing the engine in a content repository is unaffected. To match CI locally, run `python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt`; after an intentional rendering change, regenerate with `RV_UPDATE_GOLDEN=1 python -m pytest tests/test_golden.py`. `.github/dependabot.yml` asks Dependabot to propose pin bumps; a bump that changes output reddens the golden test on its own PR, where the manifest is regenerated after review.
|
|
129
|
+
|
|
130
|
+
To cut a release, follow [RELEASING.md](https://github.com/mtibbits/reVision/blob/main/RELEASING.md).
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# reVision²
|
|
2
|
+
|
|
3
|
+
A static-site build tool for teaching code by example. A reader sees a real source
|
|
4
|
+
file on the left and a lesson about it on the right. Phrases in the lesson box the
|
|
5
|
+
code they describe. A narration track drives the same boxes on a timeline.
|
|
6
|
+
|
|
7
|
+
Install the engine as in [step 1](#add-revision-to-a-repository) below, add a `revision.yaml` and a
|
|
8
|
+
`docs/revision/` folder to the repository you want to teach, and run `rv2 build`.
|
|
9
|
+
Revision control, revisited.
|
|
10
|
+
|
|
11
|
+
## Add reVision to a repository
|
|
12
|
+
|
|
13
|
+
1. Install the engine: `pip install revision-engine`. To pin a release tag instead:
|
|
14
|
+
`pip install "revision-engine @ git+https://github.com/mtibbits/reVision@v0.2.2"`, which needs `git`.
|
|
15
|
+
Graphviz (`dot`) must be on the PATH for diagrams.
|
|
16
|
+
2. Create `revision.yaml` at the repository root:
|
|
17
|
+
```yaml
|
|
18
|
+
site: { title: My project by example, base_url: "" }
|
|
19
|
+
repo: { url: https://github.com/you/project, host: github } # or host: gitlab
|
|
20
|
+
content: docs/revision
|
|
21
|
+
output: public
|
|
22
|
+
```
|
|
23
|
+
3. Create `docs/revision/curriculum.yaml` and one lesson folder holding `lesson.md`
|
|
24
|
+
and `anchors.yaml`. Copy `examples/minimal/docs/revision/` from this repository to start.
|
|
25
|
+
4. `rv2 check` to validate, `rv2 serve` to preview, `rv2 build` to write `public/`.
|
|
26
|
+
`--root` must be the repository's git root; the code pane's "open at commit" link is built from it.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
| Command | What it does |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `rv2 build` | Writes the site into the output folder. No network. Same commit, same bytes. |
|
|
33
|
+
| `rv2 check [--strict] [--list-unlinked]` | Runs every validation without writing. Use it as a pre-commit hook or CI gate. `--strict` exits 1 if any warning is reported; `--list-unlinked` lists each inline code span (not fenced blocks) that matches no vendor intrinsic table, per lesson, without changing the exit code. |
|
|
34
|
+
| `rv2 serve` | Builds to a temp folder, serves on localhost, rebuilds on change. |
|
|
35
|
+
| `rv2 narrate LESSON [--model voice.onnx]` | Synthesizes `narration.yaml` with Piper into audio plus `cues.json`. Commit both. |
|
|
36
|
+
|
|
37
|
+
For `narrate`, `pip install piper-tts` and download a voice (`.onnx` plus its `.onnx.json`) from
|
|
38
|
+
[rhasspy/piper-voices](https://huggingface.co/rhasspy/piper-voices); the example lesson uses
|
|
39
|
+
`en_US-lessac-medium`. Without `--model` the voice comes from `--voice`, then the lesson's `narration.yaml`
|
|
40
|
+
`voice`, then its front matter `voice`, then the project's `narration.voice`, and the model is `<voice>.onnx`
|
|
41
|
+
directly in `$RV2_VOICES` or `~/piper-voices`. A voice is a name, not a path: it may use only ASCII letters,
|
|
42
|
+
digits, `_`, `-` and `.`, not starting with `-` or `.`, and `rv2 narrate` refuses anything else. To use a model
|
|
43
|
+
file somewhere else, pass `--model`. `--silent` writes a silent placeholder track with plausible timings when
|
|
44
|
+
Piper is unavailable.
|
|
45
|
+
|
|
46
|
+
`revision` is an alias for `rv2`.
|
|
47
|
+
|
|
48
|
+
## Writing a lesson
|
|
49
|
+
|
|
50
|
+
- `lesson.md` starts with front matter: `title`, `file` (the source file shown), and optionally
|
|
51
|
+
`summary`, `start` (the anchor boxed on load), `media`, and `voice` (a voice name, not a path; see
|
|
52
|
+
`narrate`).
|
|
53
|
+
- `anchors.yaml` names code regions. Each anchor uses one form: `from`/`to` (a line containing
|
|
54
|
+
`from`, then the first later line matching the `to` regex), `match` (one line), or
|
|
55
|
+
`lines: 88-92`. Add `variant: true` and a `label` to list it in the Variants panel.
|
|
56
|
+
- `within: <anchor>` scopes `match`, `from` and `to` to another anchor's lines, for files where the
|
|
57
|
+
same body appears twice (aligned and unaligned variants).
|
|
58
|
+
- In the body, `[phrase](@anchor)` boxes a region, `[name](!intrinsic)` links a SIMD intrinsic to
|
|
59
|
+
its vendor documentation (Intel and Arm names to their intrinsic's page, RISC-V Vector names to
|
|
60
|
+
their section of the v1.0 intrinsic reference; a bare `` `name` `` in inline code auto-links
|
|
61
|
+
too, except inside another link's text, where it stays plain inline code; a code span in an
|
|
62
|
+
anchor phrase still links), `` inlines a Graphviz diagram (give a node
|
|
63
|
+
`id="anchor-name"` to make it hover and click like a phrase; a `URL`, `href` or HTML-label `HREF`
|
|
64
|
+
on such a node is removed with a warning, because a click on it pins the anchor), and a fenced
|
|
65
|
+
block with the language `quiz` adds a page-local knowledge check.
|
|
66
|
+
- In a quiz, a question's `q` and its `choices` accept one kind of markup: a backtick code span,
|
|
67
|
+
which renders as inline code (not linked to vendor documentation). Anything else, raw HTML and
|
|
68
|
+
backslashes included, shows as written, so a backslash does not stop a backtick from opening a
|
|
69
|
+
code span. YAML does not allow an unquoted value to begin with a backtick, so single-quote a `q`
|
|
70
|
+
or choice that starts with one (`` q: '`vaddq_f32` adds how many lanes?' ``); unquoted, the
|
|
71
|
+
build fails with "quiz block is not valid YAML". Use single quotes, not double: inside double
|
|
72
|
+
quotes YAML reads a backslash as an escape, so `` `\n` `` would lose its backslash. Inside single
|
|
73
|
+
quotes, write an apostrophe twice (`''`).
|
|
74
|
+
- A quiz block's only top-level key is `questions`. A question accepts `q`, `choices`, `answer`
|
|
75
|
+
(the index of the right choice, counting from 0) and `shuffle`; any other key fails the build
|
|
76
|
+
and is named in the error, so a misspelled `shufle: false` cannot leave a question shuffled.
|
|
77
|
+
- Each time the page loads, a question's choices appear in a random order, so the right answer is
|
|
78
|
+
not always in the same place; grading follows the choice, not its position. Add `shuffle: false`
|
|
79
|
+
to a question to keep its choices in the order written, for example numeric ranges or "all of
|
|
80
|
+
the above". A question with two choices (True/False) always keeps its order, so `shuffle: true`
|
|
81
|
+
on one is a build warning. With JavaScript off, choices appear in the order written.
|
|
82
|
+
- `narration.yaml` is a list of segments: `text` plus what to `show`, and optionally a `diagram`
|
|
83
|
+
and `node` to bring into view. Timings are derived by `rv2 narrate`, never typed. If the words
|
|
84
|
+
change and `narrate` is not rerun, `build` fails with the diff.
|
|
85
|
+
|
|
86
|
+
## Deploy
|
|
87
|
+
|
|
88
|
+
<!-- tests/test_release_doc.py::test_deploy_runs_the_strict_check_before_building checks that each platform's recipe below runs the strict check before building. -->
|
|
89
|
+
GitHub Actions: install Python and Graphviz, install the engine as in
|
|
90
|
+
[step 1](#add-revision-to-a-repository), run `rv2 check --strict` so any warning fails the job before anything is published, then
|
|
91
|
+
`rv2 build`, and upload `public/` with `actions/upload-pages-artifact`. GitLab CI: install Python,
|
|
92
|
+
Graphviz and the engine the same way and run `rv2 check --strict`, then `rv2 build`, in a `pages`
|
|
93
|
+
job with `public/` as its artifact, on an image that has `git`.
|
|
94
|
+
`.github/workflows/demo.yml` in this repository builds `examples/minimal/` that way.
|
|
95
|
+
|
|
96
|
+
## Developing the engine
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
python -m venv .venv && source .venv/Scripts/activate # or .venv/bin/activate
|
|
100
|
+
python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt && python -m playwright install chromium
|
|
101
|
+
python -m pytest --browser chromium
|
|
102
|
+
python -m playwright install firefox webkit
|
|
103
|
+
python -m pytest tests/browser --browser firefox --browser webkit
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The golden test compares the built example site byte-for-byte, except that each diagram is reduced to its nodes (ids, anchor stamps, labels) and edges (endpoints, labels), so Graphviz layout and fonts do not matter.
|
|
107
|
+
That reduction was verified identical on Graphviz 16.1.0 (Windows) and on Ubuntu 22.04's `graphviz` 2.42.2 package (which reports itself as 2.43.0); a Graphviz that changes node ids, anchor stamps, edges, or node and edge labels fails the test (graph and cluster labels and link targets are not compared).
|
|
108
|
+
CI runs on the pinned `ubuntu-24.04` image and installs its `graphviz` package (2.42.2). Its `test` job sets `RV_REQUIRE_GOLDEN=1` and runs the whole suite in Chromium, so a skipped or missing golden test fails the job; the gate's message is the last line of the log. A separate `browsers` job runs `tests/browser` in Firefox and WebKit without the golden gate; a test that cannot pass in one engine carries `@pytest.mark.xfail_browser(...)` naming its tracking issue, and is a strict xfail there. On a push to `main` that bumps the version, a `release` job tags and releases it after both pass, and later jobs upload it to TestPyPI, install it from there, and upload it to PyPI; see RELEASING.md step 4.
|
|
109
|
+
<!-- tests/test_ci_constraints.py checks that every README line installing .[dev], here and in the fence above, contains INSTALL_CMD from tests/ci_pins.py. -->
|
|
110
|
+
CI installs the runtime dependencies whose output the golden test hashes (Pygments, markdown-it-py, Jinja2, PyYAML and their dependencies) at the versions pinned in `ci-constraints-requirements.txt`; `pyproject.toml` keeps floors only, so installing the engine in a content repository is unaffected. To match CI locally, run `python -m pip install -e ".[dev]" -c ci-constraints-requirements.txt`; after an intentional rendering change, regenerate with `RV_UPDATE_GOLDEN=1 python -m pytest tests/test_golden.py`. `.github/dependabot.yml` asks Dependabot to propose pin bumps; a bump that changes output reddens the golden test on its own PR, where the manifest is regenerated after review.
|
|
111
|
+
|
|
112
|
+
To cut a release, follow [RELEASING.md](https://github.com/mtibbits/reVision/blob/main/RELEASING.md).
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT.
|