pytorial 0.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.
- pytorial-0.1/LICENSE +21 -0
- pytorial-0.1/PKG-INFO +172 -0
- pytorial-0.1/README.md +147 -0
- pytorial-0.1/pyproject.toml +60 -0
- pytorial-0.1/src/tutorial/tutorials/shell-basics.md +101 -0
- pytorial-0.1/src/tutorial/tutorials/using-tutorials.md +212 -0
- pytorial-0.1/src/tutorial/tutorials/writing-tutorials.md +414 -0
pytorial-0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Daniel Bosk
|
|
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.
|
pytorial-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytorial
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Interactive command-line tutorials with PTY-backed steps
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Daniel Bosk
|
|
8
|
+
Author-email: daniel@bosk.se
|
|
9
|
+
Maintainer: Daniel Bosk
|
|
10
|
+
Maintainer-email: dbosk@kth.se
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Topic :: Education
|
|
17
|
+
Requires-Dist: PyYAML (>=6)
|
|
18
|
+
Requires-Dist: platformdirs (>=4)
|
|
19
|
+
Requires-Dist: rich (>=12.3.0)
|
|
20
|
+
Requires-Dist: typer (>=0.9.0)
|
|
21
|
+
Project-URL: Bug Tracker, https://github.com/dbosk/tutorial/issues
|
|
22
|
+
Project-URL: Repository, https://github.com/dbosk/tutorial
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# tutorial
|
|
26
|
+
|
|
27
|
+
A Python library for interactive command-line tutorials.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pipx install tutorial
|
|
33
|
+
# or
|
|
34
|
+
uv tool install tutorial
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To run from a clone instead of an installed copy, see
|
|
38
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
The package ships with three built-in tutorials. The first one is a
|
|
43
|
+
self-guided tour of the tool itself:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
tutorial run using-tutorials
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Companion tours: `tutorial run shell-basics` (shell and editor skills)
|
|
50
|
+
and `tutorial run writing-tutorials` (authoring tutorials of your own).
|
|
51
|
+
|
|
52
|
+
### Discover what's available
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
tutorial list
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Pipe the same command into a script to get tab-separated rows instead
|
|
59
|
+
of the rich table.
|
|
60
|
+
|
|
61
|
+
### Run or resume
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
tutorial run <id> # start or resume saved progress
|
|
65
|
+
tutorial run --restart <id> # discard saved progress and start over
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Review past runs
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
tutorial review <id>
|
|
72
|
+
tutorial review <id> --step 2
|
|
73
|
+
tutorial review <id> --run-id <run-id>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Trust author-supplied shell checks
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
tutorial run --allow-shell-checks <id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
A tutorial step may declare a `check_command` that runs in your shell
|
|
83
|
+
to validate the step. This validation is opt-in: pass
|
|
84
|
+
`--allow-shell-checks` only when you trust the tutorial author.
|
|
85
|
+
|
|
86
|
+
## Writing tutorials
|
|
87
|
+
|
|
88
|
+
Take the interactive walkthrough first:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
tutorial run writing-tutorials
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### File format
|
|
95
|
+
|
|
96
|
+
A tutorial is a single Markdown file beginning with YAML front matter:
|
|
97
|
+
|
|
98
|
+
````markdown
|
|
99
|
+
---
|
|
100
|
+
id: my-tutorial
|
|
101
|
+
title: My Tutorial
|
|
102
|
+
summary: One-line description shown by `tutorial list`.
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# First step
|
|
106
|
+
|
|
107
|
+
```tutorial-step
|
|
108
|
+
required_patterns:
|
|
109
|
+
- some-command
|
|
110
|
+
hint: Try running `some-command`.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Step body in Markdown.
|
|
114
|
+
````
|
|
115
|
+
|
|
116
|
+
- Required front-matter fields: `id`, `title`, `summary`.
|
|
117
|
+
- Each top-level `# Heading` becomes one step.
|
|
118
|
+
- A step may begin with a fenced `tutorial-step` YAML block. Recognised
|
|
119
|
+
fields are `required_patterns`, `check_command`, `hint`, `edit_file`,
|
|
120
|
+
`kind`, `options`, and `answers`.
|
|
121
|
+
- Without `kind` or `edit_file`, a step opens an interactive shell.
|
|
122
|
+
- `edit_file` opens a workspace-relative file in `$EDITOR`, falling
|
|
123
|
+
back to `vim`, `vi`, or `nano` when `$EDITOR` is unset.
|
|
124
|
+
- `kind: input`, `kind: single_select`, and `kind: multi_select` prompt
|
|
125
|
+
directly in the CLI instead of opening a shell or editor. `input`
|
|
126
|
+
answers use the same string-or-`{mode, pattern}` format as
|
|
127
|
+
`required_patterns`; select questions use literal option text in
|
|
128
|
+
`options` and `answers`.
|
|
129
|
+
- `check_command` only runs when the reader passes
|
|
130
|
+
`--allow-shell-checks`.
|
|
131
|
+
|
|
132
|
+
### Share and install
|
|
133
|
+
|
|
134
|
+
Install a tutorial Markdown file or URL into the user's tutorial
|
|
135
|
+
directory:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
tutorial install path/to/tutorial.md
|
|
139
|
+
tutorial install https://example.com/tutorial.md
|
|
140
|
+
tutorial install --force path/to/tutorial.md
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Use `--force` to overwrite an installed tutorial with the same `id`.
|
|
144
|
+
The standalone CLI auto-loads installed tutorials alongside the
|
|
145
|
+
built-ins.
|
|
146
|
+
|
|
147
|
+
### Load ad hoc
|
|
148
|
+
|
|
149
|
+
To use a tutorial without installing it, point at the file or directory
|
|
150
|
+
on the command line:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
tutorial list --tutorial-path some/dir
|
|
154
|
+
tutorial run --tutorial-path some/dir <id>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`--tutorial-path` may be repeated and is appended after the built-ins.
|
|
158
|
+
|
|
159
|
+
## Embedding the CLI
|
|
160
|
+
|
|
161
|
+
The package can be mounted as a subcommand inside another Typer or
|
|
162
|
+
argparse application via `add_typer_subcommand` and
|
|
163
|
+
`add_argparse_subcommand` in `src/tutorial/cli.py`. Embedded hosts
|
|
164
|
+
prepend the `using-tutorials` lesson before host-specific tutorials, do
|
|
165
|
+
not load the user's installed tutorial directory, and hide the
|
|
166
|
+
standalone-only `install` command.
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
For the literate sources, the `make` build, and the repo layout, see
|
|
171
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
172
|
+
|
pytorial-0.1/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# tutorial
|
|
2
|
+
|
|
3
|
+
A Python library for interactive command-line tutorials.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pipx install tutorial
|
|
9
|
+
# or
|
|
10
|
+
uv tool install tutorial
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
To run from a clone instead of an installed copy, see
|
|
14
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
The package ships with three built-in tutorials. The first one is a
|
|
19
|
+
self-guided tour of the tool itself:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
tutorial run using-tutorials
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Companion tours: `tutorial run shell-basics` (shell and editor skills)
|
|
26
|
+
and `tutorial run writing-tutorials` (authoring tutorials of your own).
|
|
27
|
+
|
|
28
|
+
### Discover what's available
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
tutorial list
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Pipe the same command into a script to get tab-separated rows instead
|
|
35
|
+
of the rich table.
|
|
36
|
+
|
|
37
|
+
### Run or resume
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
tutorial run <id> # start or resume saved progress
|
|
41
|
+
tutorial run --restart <id> # discard saved progress and start over
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Review past runs
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
tutorial review <id>
|
|
48
|
+
tutorial review <id> --step 2
|
|
49
|
+
tutorial review <id> --run-id <run-id>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Trust author-supplied shell checks
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
tutorial run --allow-shell-checks <id>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A tutorial step may declare a `check_command` that runs in your shell
|
|
59
|
+
to validate the step. This validation is opt-in: pass
|
|
60
|
+
`--allow-shell-checks` only when you trust the tutorial author.
|
|
61
|
+
|
|
62
|
+
## Writing tutorials
|
|
63
|
+
|
|
64
|
+
Take the interactive walkthrough first:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
tutorial run writing-tutorials
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### File format
|
|
71
|
+
|
|
72
|
+
A tutorial is a single Markdown file beginning with YAML front matter:
|
|
73
|
+
|
|
74
|
+
````markdown
|
|
75
|
+
---
|
|
76
|
+
id: my-tutorial
|
|
77
|
+
title: My Tutorial
|
|
78
|
+
summary: One-line description shown by `tutorial list`.
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
# First step
|
|
82
|
+
|
|
83
|
+
```tutorial-step
|
|
84
|
+
required_patterns:
|
|
85
|
+
- some-command
|
|
86
|
+
hint: Try running `some-command`.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Step body in Markdown.
|
|
90
|
+
````
|
|
91
|
+
|
|
92
|
+
- Required front-matter fields: `id`, `title`, `summary`.
|
|
93
|
+
- Each top-level `# Heading` becomes one step.
|
|
94
|
+
- A step may begin with a fenced `tutorial-step` YAML block. Recognised
|
|
95
|
+
fields are `required_patterns`, `check_command`, `hint`, `edit_file`,
|
|
96
|
+
`kind`, `options`, and `answers`.
|
|
97
|
+
- Without `kind` or `edit_file`, a step opens an interactive shell.
|
|
98
|
+
- `edit_file` opens a workspace-relative file in `$EDITOR`, falling
|
|
99
|
+
back to `vim`, `vi`, or `nano` when `$EDITOR` is unset.
|
|
100
|
+
- `kind: input`, `kind: single_select`, and `kind: multi_select` prompt
|
|
101
|
+
directly in the CLI instead of opening a shell or editor. `input`
|
|
102
|
+
answers use the same string-or-`{mode, pattern}` format as
|
|
103
|
+
`required_patterns`; select questions use literal option text in
|
|
104
|
+
`options` and `answers`.
|
|
105
|
+
- `check_command` only runs when the reader passes
|
|
106
|
+
`--allow-shell-checks`.
|
|
107
|
+
|
|
108
|
+
### Share and install
|
|
109
|
+
|
|
110
|
+
Install a tutorial Markdown file or URL into the user's tutorial
|
|
111
|
+
directory:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
tutorial install path/to/tutorial.md
|
|
115
|
+
tutorial install https://example.com/tutorial.md
|
|
116
|
+
tutorial install --force path/to/tutorial.md
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use `--force` to overwrite an installed tutorial with the same `id`.
|
|
120
|
+
The standalone CLI auto-loads installed tutorials alongside the
|
|
121
|
+
built-ins.
|
|
122
|
+
|
|
123
|
+
### Load ad hoc
|
|
124
|
+
|
|
125
|
+
To use a tutorial without installing it, point at the file or directory
|
|
126
|
+
on the command line:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
tutorial list --tutorial-path some/dir
|
|
130
|
+
tutorial run --tutorial-path some/dir <id>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`--tutorial-path` may be repeated and is appended after the built-ins.
|
|
134
|
+
|
|
135
|
+
## Embedding the CLI
|
|
136
|
+
|
|
137
|
+
The package can be mounted as a subcommand inside another Typer or
|
|
138
|
+
argparse application via `add_typer_subcommand` and
|
|
139
|
+
`add_argparse_subcommand` in `src/tutorial/cli.py`. Embedded hosts
|
|
140
|
+
prepend the `using-tutorials` lesson before host-specific tutorials, do
|
|
141
|
+
not load the user's installed tutorial directory, and hide the
|
|
142
|
+
standalone-only `install` command.
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
For the literate sources, the `make` build, and the repo layout, see
|
|
147
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytorial"
|
|
3
|
+
version = "0.1"
|
|
4
|
+
description = "Interactive command-line tutorials with PTY-backed steps"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
authors = [
|
|
8
|
+
{name = "Daniel Bosk", email = "daniel@bosk.se"}
|
|
9
|
+
]
|
|
10
|
+
maintainers = [
|
|
11
|
+
{name = "Daniel Bosk", email = "dbosk@kth.se"}
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
"Intended Audience :: Education",
|
|
18
|
+
"Topic :: Education"
|
|
19
|
+
]
|
|
20
|
+
requires-python = ">=3.10"
|
|
21
|
+
dependencies = [
|
|
22
|
+
"platformdirs>=4",
|
|
23
|
+
"PyYAML>=6",
|
|
24
|
+
"rich>=12.3.0",
|
|
25
|
+
"typer>=0.9.0"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
tutorial = "tutorial.cli:app"
|
|
30
|
+
pytorial = "tutorial.cli:app"
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
repository = "https://github.com/dbosk/tutorial"
|
|
34
|
+
"Bug Tracker" = "https://github.com/dbosk/tutorial/issues"
|
|
35
|
+
|
|
36
|
+
[tool.poetry]
|
|
37
|
+
packages = [{include = "tutorial", from = "src"}]
|
|
38
|
+
include = [
|
|
39
|
+
{path = "src/**/*.py", format = "wheel"},
|
|
40
|
+
{path = "src/tutorial/tutorials/*.md"},
|
|
41
|
+
]
|
|
42
|
+
exclude = [
|
|
43
|
+
"src/tutorial/.gitignore",
|
|
44
|
+
"src/tutorial/Makefile",
|
|
45
|
+
"src/tutorial/*.nw",
|
|
46
|
+
"src/tutorial/*.tex",
|
|
47
|
+
"src/tutorial/ltxobj",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.black]
|
|
51
|
+
target-version = ["py310"]
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["poetry-core>=1.0.0"]
|
|
55
|
+
build-backend = "poetry.core.masonry.api"
|
|
56
|
+
|
|
57
|
+
[dependency-groups]
|
|
58
|
+
dev = [
|
|
59
|
+
"pytest>=7.4"
|
|
60
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: shell-basics
|
|
3
|
+
title: Shell Basics
|
|
4
|
+
summary: Use directories, files, paths, and an editor in the workspace.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Inspect the workspace
|
|
8
|
+
|
|
9
|
+
```tutorial-step
|
|
10
|
+
required_patterns:
|
|
11
|
+
- pwd
|
|
12
|
+
- ls
|
|
13
|
+
hint: The shell starts in the tutorial workspace.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Run `pwd` and `ls`.
|
|
17
|
+
|
|
18
|
+
Each tutorial run gets its own workspace. These commands show where your
|
|
19
|
+
files for this run will live.
|
|
20
|
+
|
|
21
|
+
# Create a directory
|
|
22
|
+
|
|
23
|
+
```tutorial-step
|
|
24
|
+
required_patterns:
|
|
25
|
+
- mkdir notes
|
|
26
|
+
- ls
|
|
27
|
+
- notes
|
|
28
|
+
hint: Create a directory called `notes`, then list the workspace again.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Create a directory named `notes`, then run `ls` again.
|
|
32
|
+
|
|
33
|
+
The contrast here is between the workspace before and after one directory
|
|
34
|
+
is added.
|
|
35
|
+
|
|
36
|
+
# Create a file in that directory
|
|
37
|
+
|
|
38
|
+
```tutorial-step
|
|
39
|
+
required_patterns:
|
|
40
|
+
- touch notes/todo.md
|
|
41
|
+
- ls notes
|
|
42
|
+
- todo.md
|
|
43
|
+
hint: Use `touch` with the nested path `notes/todo.md`.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Create `notes/todo.md`, then run `ls notes`.
|
|
47
|
+
|
|
48
|
+
Keep the same path stem `notes` while changing only what you create:
|
|
49
|
+
first a directory, then a file inside it.
|
|
50
|
+
|
|
51
|
+
# Edit the file
|
|
52
|
+
|
|
53
|
+
```tutorial-step
|
|
54
|
+
edit_file: notes/todo.md
|
|
55
|
+
required_patterns:
|
|
56
|
+
- tutorial authoring
|
|
57
|
+
- remember this path
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Open `notes/todo.md` in your editor and add two short lines:
|
|
61
|
+
|
|
62
|
+
- `tutorial authoring`
|
|
63
|
+
- `remember this path`
|
|
64
|
+
|
|
65
|
+
Save the file and quit the editor.
|
|
66
|
+
|
|
67
|
+
The path stays the same, but the interaction changes from shell commands
|
|
68
|
+
to an editor-backed step.
|
|
69
|
+
|
|
70
|
+
# Review the directory and the file
|
|
71
|
+
|
|
72
|
+
```tutorial-step
|
|
73
|
+
required_patterns:
|
|
74
|
+
- ls notes
|
|
75
|
+
- todo.md
|
|
76
|
+
- cat notes/todo.md
|
|
77
|
+
- tutorial authoring
|
|
78
|
+
hint: First list the directory, then display the file.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Run `ls notes` and `cat notes/todo.md`.
|
|
82
|
+
|
|
83
|
+
This contrasts two related views: listing a directory tells you which
|
|
84
|
+
files exist, while `cat` shows what one file contains.
|
|
85
|
+
|
|
86
|
+
# Generalise the pattern for tutorial files
|
|
87
|
+
|
|
88
|
+
```tutorial-step
|
|
89
|
+
required_patterns:
|
|
90
|
+
- mkdir my-tutorials
|
|
91
|
+
- touch my-tutorials/demo.md
|
|
92
|
+
- ls my-tutorials
|
|
93
|
+
- demo.md
|
|
94
|
+
hint: Repeat the same directory-and-file pattern with new names.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Create a directory named `my-tutorials`, create
|
|
98
|
+
`my-tutorials/demo.md`, then run `ls my-tutorials`.
|
|
99
|
+
|
|
100
|
+
Now the names vary, but the path pattern `directory/file.md` stays the
|
|
101
|
+
same. That is the pattern the tutorial-authoring lesson will build on.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: using-tutorials
|
|
3
|
+
title: Using Tutorials
|
|
4
|
+
summary: Learn how list, run, question prompts, and review fit together.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# List the available tutorials
|
|
8
|
+
|
|
9
|
+
```tutorial-step
|
|
10
|
+
required_patterns:
|
|
11
|
+
- tutorial list
|
|
12
|
+
- using-tutorials
|
|
13
|
+
- shell-basics
|
|
14
|
+
- writing-tutorials
|
|
15
|
+
hint: Run `tutorial list`.
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run `tutorial list`.
|
|
19
|
+
|
|
20
|
+
In a terminal, `tutorial list` renders a readable table. When the same
|
|
21
|
+
command is piped into a script, it switches to tab-separated rows.
|
|
22
|
+
|
|
23
|
+
Its main job is to show which tutorials are available and how much
|
|
24
|
+
progress is already saved for each one.
|
|
25
|
+
|
|
26
|
+
# Start another tutorial
|
|
27
|
+
|
|
28
|
+
```tutorial-step
|
|
29
|
+
required_patterns:
|
|
30
|
+
- tutorial run --restart shell-basics
|
|
31
|
+
- "shell-basics $"
|
|
32
|
+
- "Step not complete yet: Inspect the workspace"
|
|
33
|
+
hint: Start `shell-basics`, then leave its first shell with `exit`.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run `tutorial run --restart shell-basics`.
|
|
37
|
+
|
|
38
|
+
The `run` command starts or resumes saved work. We use `--restart` here so
|
|
39
|
+
this step behaves the same way even if you have already finished
|
|
40
|
+
`shell-basics` before. `--restart` discards every saved transcript for that
|
|
41
|
+
tutorial, not just the step you are about to revisit.
|
|
42
|
+
|
|
43
|
+
Because this lesson is already running, the nested shell prompt changes to
|
|
44
|
+
`shell-basics $ ` so you can tell which tutorial owns the current shell.
|
|
45
|
+
|
|
46
|
+
For this step:
|
|
47
|
+
|
|
48
|
+
1. Run `tutorial run --restart shell-basics`.
|
|
49
|
+
2. When the nested shell opens, type `exit`.
|
|
50
|
+
3. After the nested command reports that the step is not complete yet,
|
|
51
|
+
exit this outer shell too.
|
|
52
|
+
|
|
53
|
+
# Retry a shell step
|
|
54
|
+
|
|
55
|
+
```tutorial-step
|
|
56
|
+
required_patterns:
|
|
57
|
+
- "using-tutorials $"
|
|
58
|
+
- pwd
|
|
59
|
+
- ls
|
|
60
|
+
hint: First exit after `pwd`, then choose `try again` and run both commands.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Now return to this tutorial's own shell.
|
|
64
|
+
|
|
65
|
+
To feel how step validation works, try this step in two rounds:
|
|
66
|
+
|
|
67
|
+
1. First run only `pwd`, then exit the shell.
|
|
68
|
+
2. The tutorial should say the step is not complete yet.
|
|
69
|
+
3. Choose `try again`, run both `pwd` and `ls`, then exit again.
|
|
70
|
+
|
|
71
|
+
Only the latest recorded attempt counts. If you leave now and come back
|
|
72
|
+
later, `tutorial run using-tutorials` resumes from the first unfinished
|
|
73
|
+
step unless you restart it.
|
|
74
|
+
|
|
75
|
+
# Capture the workflow in a file
|
|
76
|
+
|
|
77
|
+
```tutorial-step
|
|
78
|
+
edit_file: tutorial-commands.txt
|
|
79
|
+
required_patterns:
|
|
80
|
+
- tutorial list discovers tutorials
|
|
81
|
+
- tutorial run starts or resumes one
|
|
82
|
+
- tutorial review shows saved transcripts
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Open `tutorial-commands.txt` in your editor and add these three lines:
|
|
86
|
+
|
|
87
|
+
- `tutorial list discovers tutorials`
|
|
88
|
+
- `tutorial run starts or resumes one`
|
|
89
|
+
- `tutorial review shows saved transcripts`
|
|
90
|
+
|
|
91
|
+
Save the file and quit the editor.
|
|
92
|
+
|
|
93
|
+
This contrasts a shell step with an editor step while keeping the same
|
|
94
|
+
validation idea: the tutorial only checks what was recorded after you
|
|
95
|
+
finish the step.
|
|
96
|
+
|
|
97
|
+
# Answer a one-line question
|
|
98
|
+
|
|
99
|
+
```tutorial-step
|
|
100
|
+
kind: input
|
|
101
|
+
answers:
|
|
102
|
+
- mode: regex
|
|
103
|
+
pattern: ^tutorial\s+review\s+using-tutorials(?:\s+)?$
|
|
104
|
+
hint: Type the full review command for this tutorial.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Type the exact command that reviews this tutorial:
|
|
108
|
+
`tutorial review using-tutorials`.
|
|
109
|
+
|
|
110
|
+
This is the smallest question step. The tutorial asks for one line of
|
|
111
|
+
text, records it immediately, and validates it without opening a shell.
|
|
112
|
+
|
|
113
|
+
# Pick the command that starts work
|
|
114
|
+
|
|
115
|
+
```tutorial-step
|
|
116
|
+
kind: single_select
|
|
117
|
+
options:
|
|
118
|
+
- tutorial list
|
|
119
|
+
- tutorial run
|
|
120
|
+
- tutorial review
|
|
121
|
+
answers:
|
|
122
|
+
- tutorial run
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Choose the command that starts or resumes one tutorial.
|
|
126
|
+
|
|
127
|
+
Single-select questions show numbered options. You can answer with the
|
|
128
|
+
number or with the full option text.
|
|
129
|
+
|
|
130
|
+
# Pick every read-only tutorial command
|
|
131
|
+
|
|
132
|
+
```tutorial-step
|
|
133
|
+
kind: multi_select
|
|
134
|
+
options:
|
|
135
|
+
- tutorial list
|
|
136
|
+
- tutorial run
|
|
137
|
+
- tutorial review
|
|
138
|
+
- tutorial install
|
|
139
|
+
answers:
|
|
140
|
+
- tutorial list
|
|
141
|
+
- tutorial review
|
|
142
|
+
hint: Choose the commands that only read tutorial definitions or saved state.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Choose every tutorial command here that reads existing information without
|
|
146
|
+
starting a run or installing anything.
|
|
147
|
+
|
|
148
|
+
Multi-select questions also use literal option text. The learner may pick
|
|
149
|
+
the correct options in any order, but the tutorial still stores the
|
|
150
|
+
authored answer list.
|
|
151
|
+
|
|
152
|
+
# Review the saved run
|
|
153
|
+
|
|
154
|
+
```tutorial-step
|
|
155
|
+
required_patterns:
|
|
156
|
+
- tutorial review using-tutorials
|
|
157
|
+
- Using Tutorials
|
|
158
|
+
- "Step 1: List the available tutorials"
|
|
159
|
+
- "Step 4: Capture the workflow in a file"
|
|
160
|
+
- "Step 7: Pick every read-only tutorial command"
|
|
161
|
+
- tutorial review shows saved transcripts
|
|
162
|
+
hint: Review `using-tutorials` from inside this shell.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Run `tutorial review using-tutorials`.
|
|
166
|
+
|
|
167
|
+
Review reads the saved transcripts for the current run. Because this shell
|
|
168
|
+
session is still in progress, the review output can show the earlier saved
|
|
169
|
+
steps, but it cannot show this step until after you exit the shell and the
|
|
170
|
+
step is recorded.
|
|
171
|
+
|
|
172
|
+
At this point you have seen the main run-and-review workflow end to end:
|
|
173
|
+
|
|
174
|
+
- `tutorial list` discovers what you can run
|
|
175
|
+
- `tutorial run` starts or resumes one tutorial at a time
|
|
176
|
+
- question steps can ask for input, one choice, or several choices
|
|
177
|
+
- `tutorial review` inspects the saved record afterwards
|
|
178
|
+
|
|
179
|
+
# Inspect one step of the saved run
|
|
180
|
+
|
|
181
|
+
```tutorial-step
|
|
182
|
+
required_patterns:
|
|
183
|
+
- tutorial review using-tutorials --step 1
|
|
184
|
+
- "Step 1: List the available tutorials"
|
|
185
|
+
hint: Review only step 1 from inside this shell.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Run `tutorial review using-tutorials --step 1`.
|
|
189
|
+
|
|
190
|
+
`--step N` narrows the review to one 1-indexed step instead of the whole
|
|
191
|
+
run. Review normally shows the latest saved run for that tutorial; use
|
|
192
|
+
`--run-id <id>` to pick an older one instead. `tutorial review --help`
|
|
193
|
+
shows the full form.
|
|
194
|
+
|
|
195
|
+
# Install a tutorial from a file or URL
|
|
196
|
+
|
|
197
|
+
```tutorial-step
|
|
198
|
+
kind: input
|
|
199
|
+
answers:
|
|
200
|
+
- mode: regex
|
|
201
|
+
pattern: ^tutorial\s+install\s+\S+(?:\s+--force)?\s*$
|
|
202
|
+
hint: Type an install command with a file path or URL.
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Type a command such as `tutorial install ./lesson.md` or
|
|
206
|
+
`tutorial install https://example.com/lesson.md --force`.
|
|
207
|
+
|
|
208
|
+
`tutorial install <source>` copies a tutorial Markdown file into your
|
|
209
|
+
installed tutorial directory so it appears in `tutorial list` alongside the
|
|
210
|
+
built-ins. Add `--force` to overwrite an installed tutorial with the same
|
|
211
|
+
`id`. This question step only records your answer, so you can practice the
|
|
212
|
+
command safely without installing anything.
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: writing-tutorials
|
|
3
|
+
title: Writing Tutorials
|
|
4
|
+
summary: Create a tutorial file, add shell, editor, and question steps, and load it.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Create a tutorial directory
|
|
8
|
+
|
|
9
|
+
```tutorial-step
|
|
10
|
+
required_patterns:
|
|
11
|
+
- mkdir my-tutorials
|
|
12
|
+
- ls
|
|
13
|
+
- my-tutorials
|
|
14
|
+
hint: Create `my-tutorials`, then list the workspace.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Create a directory named `my-tutorials`, then run `ls`.
|
|
18
|
+
|
|
19
|
+
This lesson keeps the path `my-tutorials/greeting.md` invariant while you
|
|
20
|
+
add one tutorial feature at a time.
|
|
21
|
+
|
|
22
|
+
# Draft the smallest tutorial
|
|
23
|
+
|
|
24
|
+
```tutorial-step
|
|
25
|
+
edit_file: my-tutorials/greeting.md
|
|
26
|
+
required_patterns:
|
|
27
|
+
- "id: greeting"
|
|
28
|
+
- "title: Greeting Tutorial"
|
|
29
|
+
- "summary: A tiny tutorial for saying hello."
|
|
30
|
+
- "# Say hello"
|
|
31
|
+
- echo hello
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Open `my-tutorials/greeting.md` and write this tutorial:
|
|
35
|
+
|
|
36
|
+
```md
|
|
37
|
+
---
|
|
38
|
+
id: greeting
|
|
39
|
+
title: Greeting Tutorial
|
|
40
|
+
summary: A tiny tutorial for saying hello.
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Say hello
|
|
44
|
+
|
|
45
|
+
Run `echo hello`.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This is the smallest useful tutorial: front matter plus one top-level
|
|
49
|
+
step.
|
|
50
|
+
|
|
51
|
+
Because this step has no `kind`, no `edit_file`, and no metadata fence,
|
|
52
|
+
the tutorial runner will open a shell for it.
|
|
53
|
+
|
|
54
|
+
# Load the tutorial from disk
|
|
55
|
+
|
|
56
|
+
```tutorial-step
|
|
57
|
+
required_patterns:
|
|
58
|
+
- tutorial list --tutorial-path my-tutorials
|
|
59
|
+
- greeting
|
|
60
|
+
hint: Point the standalone CLI at `my-tutorials`.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Run `tutorial list --tutorial-path my-tutorials`.
|
|
64
|
+
|
|
65
|
+
If the file parses, `greeting` should appear in the list.
|
|
66
|
+
|
|
67
|
+
# Add a contains match
|
|
68
|
+
|
|
69
|
+
```tutorial-step
|
|
70
|
+
edit_file: my-tutorials/greeting.md
|
|
71
|
+
required_patterns:
|
|
72
|
+
- "```tutorial-step"
|
|
73
|
+
- "required_patterns:"
|
|
74
|
+
- " - echo hello"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Keep the same tutorial and the same step, but add a metadata fence before
|
|
78
|
+
the instructions:
|
|
79
|
+
|
|
80
|
+
````md
|
|
81
|
+
# Say hello
|
|
82
|
+
|
|
83
|
+
```tutorial-step
|
|
84
|
+
required_patterns:
|
|
85
|
+
- echo hello
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run `echo hello`.
|
|
89
|
+
````
|
|
90
|
+
|
|
91
|
+
Only one aspect changed: the step now has an explicit validation rule.
|
|
92
|
+
It is still a shell step, because the metadata fence adds
|
|
93
|
+
`required_patterns` but still does not add `kind` or `edit_file`.
|
|
94
|
+
|
|
95
|
+
A bare string uses the default `contains` mode. That mode is the best
|
|
96
|
+
default because the runner strips ANSI control noise and collapses
|
|
97
|
+
whitespace before matching. Small differences such as trailing spaces
|
|
98
|
+
from shell completion do not force authors to write regexes.
|
|
99
|
+
|
|
100
|
+
# Contrast it with a regex match
|
|
101
|
+
|
|
102
|
+
```tutorial-step
|
|
103
|
+
edit_file: my-tutorials/greeting.md
|
|
104
|
+
required_patterns:
|
|
105
|
+
- "required_patterns:"
|
|
106
|
+
- " - mode: regex"
|
|
107
|
+
- ' pattern: (?m)echo hello(?:\s+)?$'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Keep the same tutorial, the same step title, and the same command, but
|
|
111
|
+
replace the string rule with an explicit regex rule:
|
|
112
|
+
|
|
113
|
+
````md
|
|
114
|
+
# Say hello
|
|
115
|
+
|
|
116
|
+
```tutorial-step
|
|
117
|
+
required_patterns:
|
|
118
|
+
- mode: regex
|
|
119
|
+
pattern: (?m)echo hello(?:\s+)?$
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Run `echo hello`.
|
|
123
|
+
````
|
|
124
|
+
|
|
125
|
+
Only one critical aspect changed: the matching mode. The command
|
|
126
|
+
`echo hello` stays invariant, while the authored rule changes from a
|
|
127
|
+
short normalized contains check to a regex that says more precisely what
|
|
128
|
+
part of the transcript should match.
|
|
129
|
+
|
|
130
|
+
Use regex when you need that extra control. Otherwise, keep the shorter
|
|
131
|
+
string form.
|
|
132
|
+
|
|
133
|
+
# Match shell output with a regex
|
|
134
|
+
|
|
135
|
+
```tutorial-step
|
|
136
|
+
edit_file: my-tutorials/greeting.md
|
|
137
|
+
required_patterns:
|
|
138
|
+
- "required_patterns:"
|
|
139
|
+
- " - mode: regex"
|
|
140
|
+
- " pattern: (?m)^hello$"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Keep the same tutorial file, the same step title, and the same
|
|
144
|
+
`echo hello` command, but change the regex so it matches the output line
|
|
145
|
+
instead of the command line:
|
|
146
|
+
|
|
147
|
+
````md
|
|
148
|
+
# Say hello
|
|
149
|
+
|
|
150
|
+
```tutorial-step
|
|
151
|
+
required_patterns:
|
|
152
|
+
- mode: regex
|
|
153
|
+
pattern: (?m)^hello$
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Run `echo hello`.
|
|
157
|
+
````
|
|
158
|
+
|
|
159
|
+
`(?m)` lets `^` and `$` mean line boundaries inside the recorded shell
|
|
160
|
+
transcript. That is where regex starts to earn its keep: `contains` can
|
|
161
|
+
only say that `hello` appears somewhere, while this rule matches one whole
|
|
162
|
+
line of shell output.
|
|
163
|
+
|
|
164
|
+
# Add an editor-backed step
|
|
165
|
+
|
|
166
|
+
```tutorial-step
|
|
167
|
+
edit_file: my-tutorials/greeting.md
|
|
168
|
+
required_patterns:
|
|
169
|
+
- "# Edit a note"
|
|
170
|
+
- "edit_file: hello.txt"
|
|
171
|
+
- hello from the editor
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Add this second step to the same file:
|
|
175
|
+
|
|
176
|
+
````md
|
|
177
|
+
# Edit a note
|
|
178
|
+
|
|
179
|
+
```tutorial-step
|
|
180
|
+
edit_file: hello.txt
|
|
181
|
+
required_patterns:
|
|
182
|
+
- hello from the editor
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Open `hello.txt` and add `hello from the editor`.
|
|
186
|
+
````
|
|
187
|
+
|
|
188
|
+
This now contrasts two different step backends in the same tutorial: a
|
|
189
|
+
shell step with explicit matching metadata and an editor-backed step with
|
|
190
|
+
`edit_file`. `edit_file` still selects the editor backend, while question
|
|
191
|
+
steps use `kind`. `edit_file` and `kind` are mutually exclusive, so
|
|
192
|
+
combining them is a parse-time error.
|
|
193
|
+
|
|
194
|
+
# Add an input question
|
|
195
|
+
|
|
196
|
+
```tutorial-step
|
|
197
|
+
edit_file: my-tutorials/greeting.md
|
|
198
|
+
required_patterns:
|
|
199
|
+
- "# Type the greeting"
|
|
200
|
+
- "kind: input"
|
|
201
|
+
- "answers:"
|
|
202
|
+
- " - mode: regex"
|
|
203
|
+
- " pattern: (?i)hello(?:\\s+)?$"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Add this third step to the same file:
|
|
207
|
+
|
|
208
|
+
````md
|
|
209
|
+
# Type the greeting
|
|
210
|
+
|
|
211
|
+
```tutorial-step
|
|
212
|
+
kind: input
|
|
213
|
+
answers:
|
|
214
|
+
- mode: regex
|
|
215
|
+
pattern: (?i)hello(?:\s+)?$
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Type `hello`.
|
|
219
|
+
````
|
|
220
|
+
|
|
221
|
+
Input questions reuse the same string-or-regex answer format as
|
|
222
|
+
`required_patterns`. Only the interaction kind changes: the runner now
|
|
223
|
+
prompts for one line of text instead of opening a shell or editor.
|
|
224
|
+
|
|
225
|
+
# Add a single-select question
|
|
226
|
+
|
|
227
|
+
```tutorial-step
|
|
228
|
+
edit_file: my-tutorials/greeting.md
|
|
229
|
+
required_patterns:
|
|
230
|
+
- "# Pick the shell command"
|
|
231
|
+
- "kind: single_select"
|
|
232
|
+
- "options:"
|
|
233
|
+
- " - echo hello"
|
|
234
|
+
- " - pwd"
|
|
235
|
+
- " - ls"
|
|
236
|
+
- "answers:"
|
|
237
|
+
- " - echo hello"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Add this fourth step to the same file:
|
|
241
|
+
|
|
242
|
+
````md
|
|
243
|
+
# Pick the shell command
|
|
244
|
+
|
|
245
|
+
```tutorial-step
|
|
246
|
+
kind: single_select
|
|
247
|
+
options:
|
|
248
|
+
- echo hello
|
|
249
|
+
- pwd
|
|
250
|
+
- ls
|
|
251
|
+
answers:
|
|
252
|
+
- echo hello
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Choose the command that prints the greeting.
|
|
256
|
+
````
|
|
257
|
+
|
|
258
|
+
Single-select questions use literal option text. The learner may answer by
|
|
259
|
+
number or by the full option, but the authored answer stays the option
|
|
260
|
+
text itself.
|
|
261
|
+
|
|
262
|
+
# Add a multi-select question
|
|
263
|
+
|
|
264
|
+
```tutorial-step
|
|
265
|
+
edit_file: my-tutorials/greeting.md
|
|
266
|
+
required_patterns:
|
|
267
|
+
- "# Pick every inspection command"
|
|
268
|
+
- "kind: multi_select"
|
|
269
|
+
- "options:"
|
|
270
|
+
- " - pwd"
|
|
271
|
+
- " - ls"
|
|
272
|
+
- " - echo hello"
|
|
273
|
+
- "answers:"
|
|
274
|
+
- " - pwd"
|
|
275
|
+
- " - ls"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Add this fifth step to the same file:
|
|
279
|
+
|
|
280
|
+
````md
|
|
281
|
+
# Pick every inspection command
|
|
282
|
+
|
|
283
|
+
```tutorial-step
|
|
284
|
+
kind: multi_select
|
|
285
|
+
options:
|
|
286
|
+
- pwd
|
|
287
|
+
- ls
|
|
288
|
+
- echo hello
|
|
289
|
+
answers:
|
|
290
|
+
- pwd
|
|
291
|
+
- ls
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Choose every command that inspects the workspace.
|
|
295
|
+
````
|
|
296
|
+
|
|
297
|
+
Multi-select questions also use literal YAML lists. The learner may choose
|
|
298
|
+
the correct options in any order, but authors still list the correct
|
|
299
|
+
option texts explicitly under `answers`.
|
|
300
|
+
|
|
301
|
+
# Add a shell check to the editor step
|
|
302
|
+
|
|
303
|
+
```tutorial-step
|
|
304
|
+
edit_file: my-tutorials/greeting.md
|
|
305
|
+
required_patterns:
|
|
306
|
+
- "check_command:"
|
|
307
|
+
- 'test "$(cat hello.txt)" = "hello from the editor"'
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Keep the same `# Edit a note` step, but add a filesystem-based shell check:
|
|
311
|
+
|
|
312
|
+
````md
|
|
313
|
+
# Edit a note
|
|
314
|
+
|
|
315
|
+
```tutorial-step
|
|
316
|
+
edit_file: hello.txt
|
|
317
|
+
required_patterns:
|
|
318
|
+
- hello from the editor
|
|
319
|
+
check_command: test "$(cat hello.txt)" = "hello from the editor"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Open `hello.txt` and add `hello from the editor`.
|
|
323
|
+
````
|
|
324
|
+
|
|
325
|
+
`check_command` is opt-in. The runner refuses to execute it unless the
|
|
326
|
+
reader passes `--allow-shell-checks`. It runs in a separate
|
|
327
|
+
`/bin/bash -lc` process with the tutorial workspace as its current
|
|
328
|
+
directory, so it can inspect files there but it does not inherit shell
|
|
329
|
+
variables or other session state from the interactive step. It is also
|
|
330
|
+
AND-ed with `required_patterns`: both checks must pass before the step
|
|
331
|
+
completes.
|
|
332
|
+
|
|
333
|
+
# Test the tutorial you wrote
|
|
334
|
+
|
|
335
|
+
```tutorial-step
|
|
336
|
+
required_patterns:
|
|
337
|
+
- tutorial run --tutorial-path my-tutorials greeting
|
|
338
|
+
- "greeting $"
|
|
339
|
+
- "Step not complete yet: Say hello"
|
|
340
|
+
hint: Start `greeting`, then leave its first shell with `exit`.
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Run `tutorial run --tutorial-path my-tutorials greeting`.
|
|
344
|
+
|
|
345
|
+
This still tests a different aspect from `tutorial list --tutorial-path
|
|
346
|
+
my-tutorials`. Listing only proves that the file parses and the tutorial is
|
|
347
|
+
discoverable. Running it proves that the authored tutorial still starts
|
|
348
|
+
with the expected shell backend even after you add editor and question
|
|
349
|
+
steps later in the file.
|
|
350
|
+
|
|
351
|
+
Keep the tutorial file itself invariant here. Only the command changes: you
|
|
352
|
+
now move from inspecting tutorial metadata to exercising the authored
|
|
353
|
+
behaviour.
|
|
354
|
+
|
|
355
|
+
For this step:
|
|
356
|
+
|
|
357
|
+
1. Run `tutorial run --tutorial-path my-tutorials greeting`.
|
|
358
|
+
2. When the nested shell opens with the `greeting $ ` prompt, type `exit`.
|
|
359
|
+
3. The nested run should report `Step not complete yet: Say hello`.
|
|
360
|
+
4. Exit this lesson's shell too so the transcript is recorded.
|
|
361
|
+
|
|
362
|
+
# Iterate on one step with `tutorial develop`
|
|
363
|
+
|
|
364
|
+
```tutorial-step
|
|
365
|
+
required_patterns:
|
|
366
|
+
- tutorial develop --tutorial-path my-tutorials greeting 1
|
|
367
|
+
- "greeting $"
|
|
368
|
+
- "Verdict: FAIL"
|
|
369
|
+
hint: Start develop for step 1, exit immediately, then keep the FAIL verdict and stop.
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Run `tutorial develop --tutorial-path my-tutorials greeting 1`.
|
|
373
|
+
|
|
374
|
+
For this step:
|
|
375
|
+
|
|
376
|
+
1. Run `tutorial develop --tutorial-path my-tutorials greeting 1`.
|
|
377
|
+
2. When the temporary `greeting $ ` shell opens, type `exit` immediately.
|
|
378
|
+
3. Answer yes when `tutorial develop` asks whether the `FAIL` verdict is
|
|
379
|
+
correct.
|
|
380
|
+
4. Answer no when it asks whether to run the step again.
|
|
381
|
+
|
|
382
|
+
`tutorial develop` exercises one authored step in a temporary workspace,
|
|
383
|
+
prints `PASS` or `FAIL`, and lets you edit and re-run that step without
|
|
384
|
+
restarting the whole tutorial. We point at step `1` by number here, but the
|
|
385
|
+
same command also accepts a regex that matches the step title. Develop also
|
|
386
|
+
enables shell checks by default; pass `--deny-shell-checks` to turn them
|
|
387
|
+
off for one session.
|
|
388
|
+
|
|
389
|
+
# Inspect the finished tutorial
|
|
390
|
+
|
|
391
|
+
```tutorial-step
|
|
392
|
+
required_patterns:
|
|
393
|
+
- tutorial list --tutorial-path my-tutorials
|
|
394
|
+
- greeting
|
|
395
|
+
- cat my-tutorials/greeting.md
|
|
396
|
+
- " pattern: (?m)^hello$"
|
|
397
|
+
- "kind: input"
|
|
398
|
+
- "kind: single_select"
|
|
399
|
+
- "kind: multi_select"
|
|
400
|
+
- "edit_file: hello.txt"
|
|
401
|
+
- 'check_command: test "$(cat hello.txt)" = "hello from the editor"'
|
|
402
|
+
- " pattern: (?i)hello(?:\\s+)?$"
|
|
403
|
+
hint: List the tutorials, then display the file you wrote.
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Run `tutorial list --tutorial-path my-tutorials` and
|
|
407
|
+
`cat my-tutorials/greeting.md`.
|
|
408
|
+
|
|
409
|
+
At this point you have checked the same tutorial in three different ways:
|
|
410
|
+
|
|
411
|
+
- `tutorial list --tutorial-path my-tutorials` shows that it loads
|
|
412
|
+
- `tutorial run --tutorial-path my-tutorials greeting` shows that it runs
|
|
413
|
+
- `cat my-tutorials/greeting.md` lets you inspect the final shell, editor,
|
|
414
|
+
shell-check, and question metadata directly
|