paulrun 0.1.0__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.
- paulrun-0.1.0/.github/workflows/ci.yml +59 -0
- paulrun-0.1.0/.gitignore +20 -0
- paulrun-0.1.0/.python-version +1 -0
- paulrun-0.1.0/CHANGELOG.md +18 -0
- paulrun-0.1.0/CODE_OF_CONDUCT.md +32 -0
- paulrun-0.1.0/CONTRIBUTING.md +150 -0
- paulrun-0.1.0/CONTRIBUTORS.md +3 -0
- paulrun-0.1.0/LICENSE +21 -0
- paulrun-0.1.0/Makefile +42 -0
- paulrun-0.1.0/PKG-INFO +137 -0
- paulrun-0.1.0/PUBLISHING.md +134 -0
- paulrun-0.1.0/README.md +120 -0
- paulrun-0.1.0/SECURITY.md +11 -0
- paulrun-0.1.0/docs/CHANGELOG.md +18 -0
- paulrun-0.1.0/docs/backends.md +258 -0
- paulrun-0.1.0/docs/cli.md +228 -0
- paulrun-0.1.0/docs/index.md +110 -0
- paulrun-0.1.0/docs/install.md +42 -0
- paulrun-0.1.0/docs/runbook-format.md +267 -0
- paulrun-0.1.0/examples/clientele-publishing.md +160 -0
- paulrun-0.1.0/examples/hello.md +30 -0
- paulrun-0.1.0/mkdocs.yml +47 -0
- paulrun-0.1.0/paulrun/__init__.py +0 -0
- paulrun-0.1.0/paulrun/backends/__init__.py +178 -0
- paulrun-0.1.0/paulrun/backends/python.py +51 -0
- paulrun-0.1.0/paulrun/backends/shell.py +80 -0
- paulrun-0.1.0/paulrun/cli.py +233 -0
- paulrun-0.1.0/paulrun/inputs.py +74 -0
- paulrun-0.1.0/paulrun/runbook.py +199 -0
- paulrun-0.1.0/paulrun/runner.py +295 -0
- paulrun-0.1.0/paulrun/settings.py +3 -0
- paulrun-0.1.0/paulrun/transcript.py +136 -0
- paulrun-0.1.0/paulrun/validate.py +257 -0
- paulrun-0.1.0/pyproject.toml +78 -0
- paulrun-0.1.0/specs/SPEC.md +370 -0
- paulrun-0.1.0/tasks/plan.md +121 -0
- paulrun-0.1.0/tasks/todo.md +488 -0
- paulrun-0.1.0/tests/__init__.py +0 -0
- paulrun-0.1.0/tests/backends/__init__.py +0 -0
- paulrun-0.1.0/tests/backends/test_python.py +205 -0
- paulrun-0.1.0/tests/backends/test_shell.py +336 -0
- paulrun-0.1.0/tests/conftest.py +14 -0
- paulrun-0.1.0/tests/runbooks/all_features.md +72 -0
- paulrun-0.1.0/tests/runbooks/broken/block-before-first-step.md +15 -0
- paulrun-0.1.0/tests/runbooks/broken/duplicate-input-name.md +14 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-before-first-step.md +14 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-env-used-before.md +20 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-in-output-path.md +15 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-invalid-yaml.md +10 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-missing-description.md +13 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-name-already-declared.md +17 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-not-a-mapping.md +10 -0
- paulrun-0.1.0/tests/runbooks/broken/input-block-placeholder-used-before.md +20 -0
- paulrun-0.1.0/tests/runbooks/broken/input-missing-description.md +11 -0
- paulrun-0.1.0/tests/runbooks/broken/input-missing-name.md +11 -0
- paulrun-0.1.0/tests/runbooks/broken/input-name-not-upper-snake-case.md +12 -0
- paulrun-0.1.0/tests/runbooks/broken/invalid-pattern.md +13 -0
- paulrun-0.1.0/tests/runbooks/broken/missing-title.md +9 -0
- paulrun-0.1.0/tests/runbooks/broken/python-not-on-path.md +10 -0
- paulrun-0.1.0/tests/runbooks/broken/python-syntax-error.md +11 -0
- paulrun-0.1.0/tests/runbooks/broken/run-block-with-no-backend.md +9 -0
- paulrun-0.1.0/tests/runbooks/broken/secret-as-placeholder.md +13 -0
- paulrun-0.1.0/tests/runbooks/broken/shell-syntax-error.md +11 -0
- paulrun-0.1.0/tests/runbooks/broken/title-not-a-string.md +11 -0
- paulrun-0.1.0/tests/runbooks/broken/undeclared-placeholder-in-confirm.md +9 -0
- paulrun-0.1.0/tests/runbooks/broken/undeclared-placeholder-in-docstring.md +9 -0
- paulrun-0.1.0/tests/runbooks/broken/undeclared-placeholder-in-output-path.md +10 -0
- paulrun-0.1.0/tests/runbooks/broken/undeclared-placeholder.md +10 -0
- paulrun-0.1.0/tests/runbooks/broken-body.md +21 -0
- paulrun-0.1.0/tests/runbooks/docstring-and-confirm-example.md +42 -0
- paulrun-0.1.0/tests/runbooks/dry-run-example.md +43 -0
- paulrun-0.1.0/tests/runbooks/input-block-example.md +53 -0
- paulrun-0.1.0/tests/runbooks/inputs-example.md +42 -0
- paulrun-0.1.0/tests/runbooks/minimal.md +9 -0
- paulrun-0.1.0/tests/runbooks/python-example.md +45 -0
- paulrun-0.1.0/tests/runbooks/step-example.md +37 -0
- paulrun-0.1.0/tests/runbooks/transcript-example.md +44 -0
- paulrun-0.1.0/tests/runbooks/two_steps.md +19 -0
- paulrun-0.1.0/tests/test_backend_loading.py +132 -0
- paulrun-0.1.0/tests/test_cli.py +1173 -0
- paulrun-0.1.0/tests/test_docs.py +165 -0
- paulrun-0.1.0/tests/test_examples.py +84 -0
- paulrun-0.1.0/tests/test_inputs.py +147 -0
- paulrun-0.1.0/tests/test_interrupt.py +292 -0
- paulrun-0.1.0/tests/test_runbook.py +666 -0
- paulrun-0.1.0/tests/test_runner.py +1430 -0
- paulrun-0.1.0/tests/test_transcript.py +459 -0
- paulrun-0.1.0/tests/test_validate.py +619 -0
- paulrun-0.1.0/uv.lock +1086 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI - Lint and Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.12", "3.13", "3.14", "3.15-dev"]
|
|
15
|
+
steps:
|
|
16
|
+
#----------------------------------------------
|
|
17
|
+
# check-out repo and set-up python
|
|
18
|
+
#----------------------------------------------
|
|
19
|
+
- name: Check out repository
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
- name: Set up python
|
|
22
|
+
id: setup-python
|
|
23
|
+
uses: actions/setup-python@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{matrix.python-version}}
|
|
26
|
+
#----------------------------------------------
|
|
27
|
+
# ----- install & configure uv -----
|
|
28
|
+
#----------------------------------------------
|
|
29
|
+
# Pointing uv at the interpreter setup-python installed overrides .python-version,
|
|
30
|
+
# which would otherwise make every matrix entry run on 3.14.
|
|
31
|
+
- name: Install UV
|
|
32
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
33
|
+
with:
|
|
34
|
+
python-version: ${{ steps.setup-python.outputs.python-path }}
|
|
35
|
+
enable-cache: true
|
|
36
|
+
cache-dependency-glob: "uv.lock"
|
|
37
|
+
|
|
38
|
+
#----------------------------------------------
|
|
39
|
+
# install dependencies
|
|
40
|
+
#----------------------------------------------
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: uv sync --frozen
|
|
43
|
+
- name: Show Python version
|
|
44
|
+
run: uv run python --version
|
|
45
|
+
#----------------------------------------------
|
|
46
|
+
# Run linting and tests
|
|
47
|
+
#----------------------------------------------
|
|
48
|
+
- name: Ruff format check
|
|
49
|
+
run: |
|
|
50
|
+
uv run ruff format --check .
|
|
51
|
+
- name: Ruff linting
|
|
52
|
+
run: |
|
|
53
|
+
uv run ruff check .
|
|
54
|
+
- name: Type check with ty
|
|
55
|
+
run: |
|
|
56
|
+
uv run ty check
|
|
57
|
+
- name: Test with pytest
|
|
58
|
+
run: |
|
|
59
|
+
uv run pytest -vv --cov=paulrun --cov-branch --cov-report=term-missing --cov-config=pyproject.toml
|
paulrun-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
.ty_cache/
|
|
3
|
+
.ruff_cache/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.idea/
|
|
6
|
+
__pycache__
|
|
7
|
+
.DS_Store
|
|
8
|
+
dist/
|
|
9
|
+
site/
|
|
10
|
+
htmlcov/
|
|
11
|
+
*.coverage*
|
|
12
|
+
coverage.xml
|
|
13
|
+
.cache/
|
|
14
|
+
|
|
15
|
+
# Transcripts written by running the fixture runbooks by hand
|
|
16
|
+
tests/runbooks/logs/
|
|
17
|
+
|
|
18
|
+
# Transcripts written by releasing paulrun with PUBLISHING.md, or running the examples by hand
|
|
19
|
+
/logs/
|
|
20
|
+
examples/logs/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Change log
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
Initial release. paulrun runs hand-written markdown runbooks.
|
|
6
|
+
|
|
7
|
+
- `paulrun go` runs a runbook's steps in order, streaming each block's output as it's printed and stopping at the first block that fails.
|
|
8
|
+
- `run` blocks for shell (`sh`, `bash`, `shell`) and Python (`python`, `py`), with the `python` frontmatter key to choose the interpreter.
|
|
9
|
+
- `docstring` blocks to show notes as the runbook runs, and `confirm` blocks to pause for steps done by hand.
|
|
10
|
+
- `input` blocks to ask for a value part way through a run, once it exists: a pull request number from a step done by hand, or a one-time code that expires in seconds.
|
|
11
|
+
- Inputs, collected from the environment or by asking, with an optional `pattern` to check them against. Plain inputs fill in `<NAME>` placeholders; secrets are hidden as you type them and replaced with `****` everywhere paulrun shows or records them.
|
|
12
|
+
- `paulrun go --dry` to see every block with its inputs filled in, without running anything.
|
|
13
|
+
- `paulrun go --step` to run, skip or quit at each block.
|
|
14
|
+
- Transcripts: with `output_path` set, every run is added to a plain record of what ran, what it printed and how it finished.
|
|
15
|
+
- Ctrl-C stops a run cleanly, letting a running block stop and recording how far the run got.
|
|
16
|
+
- `paulrun check` to find problems before running anything: missing or malformed frontmatter, undeclared placeholders, secrets used as placeholders, blocks outside a step, and shell and Python syntax errors, with shellcheck warnings when it's installed.
|
|
17
|
+
- `paulrun inputs` to list the inputs a runbook needs, and `paulrun backends` to list the languages paulrun can run.
|
|
18
|
+
- Backends for other languages can be added by any package, through the `paulrun.backends` entry point group.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# paulrun Code of Conduct
|
|
2
|
+
|
|
3
|
+
Like the technical community as a whole, the paulrun team and community is made up of a mixture of professionals and volunteers from all over the world, working on every aspect of the mission - including mentorship, teaching, and connecting people.
|
|
4
|
+
|
|
5
|
+
Diversity is one of our huge strengths, but it can also lead to communication issues and unhappiness. To that end, we have a few ground rules that we ask people to adhere to. This code applies equally to founders, mentors and those seeking help and guidance.
|
|
6
|
+
|
|
7
|
+
This isn’t an exhaustive list of things that you can’t do. Rather, take it in the spirit in which it’s intended - a guide to make it easier to enrich all of us and the technical communities in which we participate.
|
|
8
|
+
|
|
9
|
+
This code of conduct applies to all spaces managed by the paulrun project or Paul Hallett. This includes the issue tracker, pull requests, discussions, and any other forums created by the project team which the community uses for communication. In addition, violations of this code outside these spaces may affect a person's ability to participate within them.
|
|
10
|
+
|
|
11
|
+
If you believe someone is violating the code of conduct, we ask that you report it by emailing [paulandrewhallett@gmail.com](mailto:paulandrewhallett@gmail.com).
|
|
12
|
+
|
|
13
|
+
- **Be friendly and patient.**
|
|
14
|
+
- **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
|
|
15
|
+
- **Be considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
|
|
16
|
+
- **Be respectful.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of the paulrun community should be respectful when dealing with other members as well as with people outside the paulrun community.
|
|
17
|
+
- **Be careful in the words that you choose.** We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. This includes, but is not limited to:
|
|
18
|
+
- Violent threats or language directed against another person.
|
|
19
|
+
- Discriminatory jokes and language.
|
|
20
|
+
- Posting sexually explicit or violent material.
|
|
21
|
+
- Posting (or threatening to post) other people's personally identifying information ("doxing").
|
|
22
|
+
- Personal insults, especially those using racist or sexist terms.
|
|
23
|
+
- Unwelcome sexual attention.
|
|
24
|
+
- Advocating for, or encouraging, any of the above behavior.
|
|
25
|
+
- Repeated harassment of others. In general, if someone asks you to stop, then stop.
|
|
26
|
+
- **When we disagree, try to understand why.** Disagreements, both social and technical, happen all the time and paulrun is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of paulrun comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
|
|
27
|
+
|
|
28
|
+
Original text courtesy of the [Speak Up! project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html).
|
|
29
|
+
|
|
30
|
+
## Questions?
|
|
31
|
+
|
|
32
|
+
If you have questions, please see [CONTRIBUTING.md](CONTRIBUTING.md). If that doesn't answer your questions, feel free to [contact us](mailto:paulandrewhallett@gmail.com).
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
First things first: thank you for contributing! This project will be successful thanks to everyone who contributes, and we're happy to have you.
|
|
4
|
+
|
|
5
|
+
## AI disclosure
|
|
6
|
+
|
|
7
|
+
Within paulrun we use coding agents to prototype and build out ideas, and then take the idea to fruition using human reviewed and approved code.
|
|
8
|
+
|
|
9
|
+
You, the human and the author of changes are responsible for the contributions, and if you cannot confidently explain or demonstrate understanding of the changes you are contributing, then they will not be accepted.
|
|
10
|
+
|
|
11
|
+
## Bug or issue?
|
|
12
|
+
|
|
13
|
+
To raise a bug or issue please use [our GitHub](https://github.com/phalt/paulrun/issues).
|
|
14
|
+
|
|
15
|
+
Please check the issue has not been raised before by using the search feature.
|
|
16
|
+
|
|
17
|
+
When submitting an issue or bug, please make sure you provide thorough detail on:
|
|
18
|
+
|
|
19
|
+
1. The version of paulrun you are using (`paulrun --version`)
|
|
20
|
+
2. Your operating system and Python version
|
|
21
|
+
3. Any errors or outputs you see in your terminal
|
|
22
|
+
4. The smallest runbook that shows the problem (this is particularly important). Take out any secrets first!
|
|
23
|
+
|
|
24
|
+
## Contribution
|
|
25
|
+
|
|
26
|
+
If you want to directly contribute you can do so in two ways:
|
|
27
|
+
|
|
28
|
+
1. Documentation
|
|
29
|
+
2. Code
|
|
30
|
+
|
|
31
|
+
### Documentation
|
|
32
|
+
|
|
33
|
+
We use [mkdocs](https://www.mkdocs.org/) and [GitHub pages](https://pages.github.com/) to deploy our docs.
|
|
34
|
+
|
|
35
|
+
Fixing grammar, spelling mistakes, or expanding the documentation to cover features that are not yet documented, are all valuable contributions.
|
|
36
|
+
|
|
37
|
+
Please see the **Set up** instructions below to run the docs locally on your computer, then:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
make docs-serve
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The examples in the docs and the README are tested, so if you change one, run `make test` too.
|
|
44
|
+
|
|
45
|
+
### Code
|
|
46
|
+
|
|
47
|
+
Contribution by writing code for new features, or fixing bugs, is a great way to contribute to the project.
|
|
48
|
+
|
|
49
|
+
Please [open an issue](https://github.com/phalt/paulrun/issues) to talk it through before changing the runbook format (frontmatter keys, what a fence does, placeholders or the transcript), adding a command or flag, or adding a dependency. These are hard to change once people's runbooks rely on them.
|
|
50
|
+
|
|
51
|
+
#### Set up
|
|
52
|
+
|
|
53
|
+
Clone the repo:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
git clone git@github.com:phalt/paulrun.git
|
|
57
|
+
cd paulrun
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Move to a feature branch:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
git switch -c my-branch-name
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Install UV (if not already installed):
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
# On macOS and Linux:
|
|
70
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
71
|
+
|
|
72
|
+
# Or using pip:
|
|
73
|
+
pip install uv
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Install all the dependencies:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
make install
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This will use UV to create a virtual environment and install all dependencies, including paulrun itself, so its built-in backends are registered. UV handles the virtual environment automatically, so you don't need to manually activate it.
|
|
83
|
+
|
|
84
|
+
To make sure you have things set up correctly, please run the tests:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
make test
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
You can run your copy of paulrun with `uv run paulrun`, e.g. on one of the example runbooks in `tests/runbooks/`:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
uv run paulrun go tests/runbooks/two_steps.md
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run `make help` to see every command.
|
|
97
|
+
|
|
98
|
+
### Adding a backend
|
|
99
|
+
|
|
100
|
+
paulrun runs shell and Python blocks out of the box. To add another language to paulrun itself:
|
|
101
|
+
|
|
102
|
+
1. Write `paulrun/backends/<name>.py`, implementing the backend protocol
|
|
103
|
+
2. Add one entry point line for it in `pyproject.toml`, then run `make install`
|
|
104
|
+
3. Write `tests/backends/test_<name>.py`, which really runs blocks (skipped if the language isn't installed)
|
|
105
|
+
4. Add a section about it to `docs/backends.md`
|
|
106
|
+
5. Add a CHANGELOG entry
|
|
107
|
+
|
|
108
|
+
[docs/backends.md](https://phalt.github.io/paulrun/backends/) explains each step, and how backends work.
|
|
109
|
+
|
|
110
|
+
A backend doesn't have to be part of paulrun: the same page shows how to ship one in your own package.
|
|
111
|
+
|
|
112
|
+
### Preparing changes for review
|
|
113
|
+
|
|
114
|
+
Once you've made changes, here's a good checklist to run through before publishing for review:
|
|
115
|
+
|
|
116
|
+
Format and lint the code:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
make format
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Check the types:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
make ty
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Run the tests:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
make test
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
New behaviour needs tests, and each new runbook feature gets an example runbook in `tests/runbooks/` that shows it off.
|
|
135
|
+
|
|
136
|
+
If you've changed how runbooks are written or run, update `docs/runbook-format.md` or `docs/cli.md` to match.
|
|
137
|
+
|
|
138
|
+
Make sure you add to `CHANGELOG.md` and `docs/CHANGELOG.md` what changes you have made. The two must be identical, and a test checks they are.
|
|
139
|
+
|
|
140
|
+
Make sure you add your name to `CONTRIBUTORS.md` as well!
|
|
141
|
+
|
|
142
|
+
### Making a pull request
|
|
143
|
+
|
|
144
|
+
Please push your changes up to a feature branch and make a new [pull request](https://github.com/phalt/paulrun/compare) on GitHub.
|
|
145
|
+
|
|
146
|
+
Please add a description to the PR and some information about why the change is being made.
|
|
147
|
+
|
|
148
|
+
After a review, you might need to make more changes.
|
|
149
|
+
|
|
150
|
+
Once accepted, a core contributor will merge your changes!
|
paulrun-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paul Hallett
|
|
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.
|
paulrun-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
help:
|
|
2
|
+
@echo Developer commands for paulrun
|
|
3
|
+
@echo
|
|
4
|
+
@grep -E '^[ .a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
|
5
|
+
@echo
|
|
6
|
+
|
|
7
|
+
install: ## Install requirements ready for development
|
|
8
|
+
uv sync
|
|
9
|
+
|
|
10
|
+
deploy-docs: ## Build and deploy the documentation
|
|
11
|
+
uv run mkdocs build
|
|
12
|
+
uv run mkdocs gh-deploy
|
|
13
|
+
|
|
14
|
+
docs-serve: ## Run a local documentation server
|
|
15
|
+
uv run mkdocs serve
|
|
16
|
+
|
|
17
|
+
release: ## Build a new version and release it
|
|
18
|
+
uv build
|
|
19
|
+
uv publish
|
|
20
|
+
|
|
21
|
+
ty: ## Run a static syntax check
|
|
22
|
+
uv run ty check .
|
|
23
|
+
|
|
24
|
+
format: ## Format the code correctly
|
|
25
|
+
uv run ruff format .
|
|
26
|
+
uv run ruff check --fix .
|
|
27
|
+
|
|
28
|
+
clean: ## Clear any cache files and test files
|
|
29
|
+
rm -rf .ty_cache
|
|
30
|
+
rm -rf .pytest_cache
|
|
31
|
+
rm -rf .ruff_cache
|
|
32
|
+
rm -rf site/
|
|
33
|
+
rm -rf dist/
|
|
34
|
+
find paulrun tests -name __pycache__ -prune -exec rm -rf {} +
|
|
35
|
+
rm -rf htmlcov/
|
|
36
|
+
rm -rf .coverage
|
|
37
|
+
|
|
38
|
+
test: ## Run tests
|
|
39
|
+
uv run pytest -vvv -x --cov=paulrun --cov-report=term-missing --cov-report=html --cov-config=pyproject.toml
|
|
40
|
+
|
|
41
|
+
shell: ## Run an ipython shell
|
|
42
|
+
uv run ipython
|
paulrun-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: paulrun
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Run hand-written markdown runbooks
|
|
5
|
+
Project-URL: Homepage, https://github.com/phalt/paulrun
|
|
6
|
+
Project-URL: Documentation, https://phalt.github.io/paulrun/
|
|
7
|
+
Project-URL: Issues, https://github.com/phalt/paulrun/issues
|
|
8
|
+
Author-email: Paul Hallett <paulandrewhallett@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Requires-Dist: click>=8.5.0
|
|
13
|
+
Requires-Dist: markdown-it-py>=4.2.0
|
|
14
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
15
|
+
Requires-Dist: rich>=15.0.0
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# 🏃♂️➡️📕 paulrun
|
|
19
|
+
|
|
20
|
+
## What is paulrun?
|
|
21
|
+
|
|
22
|
+
paulrun runs hand-written markdown runbooks.
|
|
23
|
+
|
|
24
|
+
A runbook is a normal markdown file that a person can read and follow without paulrun. Mark a code block `run`, and paulrun runs it for you. Everything else stays documentation.
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/paulrun)
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
## Example runbook
|
|
31
|
+
|
|
32
|
+
Save this as `hello.md`:
|
|
33
|
+
|
|
34
|
+
````markdown
|
|
35
|
+
---
|
|
36
|
+
title: Say hello
|
|
37
|
+
inputs:
|
|
38
|
+
- name: NAME
|
|
39
|
+
description: Who to say hello to
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# Say hello
|
|
43
|
+
|
|
44
|
+
A runbook is just markdown. paulrun runs the blocks marked `run`, shows the
|
|
45
|
+
`docstring` ones, and leaves everything else alone, like this paragraph.
|
|
46
|
+
|
|
47
|
+
## Greet
|
|
48
|
+
|
|
49
|
+
```docstring
|
|
50
|
+
Saying hello to <NAME>.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```sh run
|
|
54
|
+
echo "Hello, <NAME>!"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Count the letters
|
|
58
|
+
|
|
59
|
+
```python run
|
|
60
|
+
import os
|
|
61
|
+
|
|
62
|
+
name = os.environ["NAME"]
|
|
63
|
+
print(f"{name} has {len(name)} letters")
|
|
64
|
+
```
|
|
65
|
+
````
|
|
66
|
+
|
|
67
|
+
Check it, then run it:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
paulrun check hello.md
|
|
71
|
+
paulrun go hello.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
hello.md: ok
|
|
76
|
+
NAME (Who to say hello to): Paul
|
|
77
|
+
Say hello
|
|
78
|
+
inputs: NAME=Paul
|
|
79
|
+
1. Greet
|
|
80
|
+
2. Count the letters
|
|
81
|
+
2 run blocks
|
|
82
|
+
Start? [y/N] y
|
|
83
|
+
|
|
84
|
+
=== Greet ===
|
|
85
|
+
Saying hello to Paul.
|
|
86
|
+
echo "Hello, Paul!"
|
|
87
|
+
Hello, Paul!
|
|
88
|
+
exit 0 (0.0s)
|
|
89
|
+
|
|
90
|
+
=== Count the letters ===
|
|
91
|
+
import os
|
|
92
|
+
|
|
93
|
+
name = os.environ["NAME"]
|
|
94
|
+
print(f"{name} has {len(name)} letters")
|
|
95
|
+
Paul has 4 letters
|
|
96
|
+
exit 0 (0.1s)
|
|
97
|
+
|
|
98
|
+
finished: ok (0.1s)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`check` finds problems before anything runs. `go` asks for the inputs, shows you the plan, then runs each step in order, streaming every command's output as it goes. It stops at the first command that fails.
|
|
102
|
+
|
|
103
|
+
## Why use paulrun?
|
|
104
|
+
|
|
105
|
+
Release steps get forgotten. Writing them down helps; making the written-down version run itself fixes it.
|
|
106
|
+
|
|
107
|
+
- **It's just markdown** - A runbook reads like documentation, renders on GitHub, and works fine without paulrun.
|
|
108
|
+
- **Check before you run** - `paulrun check` catches undeclared placeholders, broken frontmatter and shell or Python syntax errors before anything runs.
|
|
109
|
+
- **See what would happen** - `paulrun go --dry` shows every command with its inputs filled in, and runs nothing.
|
|
110
|
+
- **Inputs and secrets** - Ask for values once, check them against a pattern, and keep secrets off the screen and out of the log.
|
|
111
|
+
- **Manual steps too** - `confirm` blocks pause for the things only a person can do.
|
|
112
|
+
- **A record of every run** - An optional transcript keeps every command, its output and how the run finished.
|
|
113
|
+
- **More than shell** - Shell and Python blocks are built in, and other languages can be added as backends.
|
|
114
|
+
|
|
115
|
+
## Install
|
|
116
|
+
|
|
117
|
+
paulrun is a command-line tool, so install it on its own rather than into a project:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
uv tool install paulrun
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Or with pip:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
pip install paulrun
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
paulrun needs Python 3.12 or later and `bash`, and runs on **macOS and Linux**. Windows isn't supported. It's for runbooks you run yourself, at a terminal; there's no CI mode.
|
|
130
|
+
|
|
131
|
+
## Getting started
|
|
132
|
+
|
|
133
|
+
👉 Read the [full documentation](https://phalt.github.io/paulrun/) for the runbook format, every command, and writing your own backend.
|
|
134
|
+
|
|
135
|
+
See [`examples/`](https://github.com/phalt/paulrun/tree/main/examples) for more runbooks, including a real release, and [`PUBLISHING.md`](https://github.com/phalt/paulrun/blob/main/PUBLISHING.md) for the runbook paulrun releases itself with.
|
|
136
|
+
|
|
137
|
+
Want to help? See [CONTRIBUTING.md](https://github.com/phalt/paulrun/blob/main/CONTRIBUTING.md).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Release paulrun
|
|
3
|
+
description: Release a new version of paulrun to GitHub, the docs site and PyPI.
|
|
4
|
+
output_path: logs/release-<VERSION>.md
|
|
5
|
+
inputs:
|
|
6
|
+
- name: VERSION
|
|
7
|
+
description: The version to release, e.g. 0.2.0
|
|
8
|
+
pattern: '^\d+\.\d+\.\d+$'
|
|
9
|
+
- name: UV_PUBLISH_TOKEN
|
|
10
|
+
description: PyPI API token for paulrun
|
|
11
|
+
secret: true
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# 🏃♂️➡️📕 Releasing paulrun
|
|
15
|
+
|
|
16
|
+
paulrun releases itself with this runbook:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
paulrun check PUBLISHING.md
|
|
20
|
+
paulrun go PUBLISHING.md --dry
|
|
21
|
+
paulrun go PUBLISHING.md
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
You can also follow it by hand: each step's commands are in the order they run.
|
|
25
|
+
|
|
26
|
+
Before you start, add a `## <version>` section for the release to the top of
|
|
27
|
+
`CHANGELOG.md`, and copy it to `docs/CHANGELOG.md`. That change gets committed
|
|
28
|
+
with the version bump.
|
|
29
|
+
|
|
30
|
+
Each run is recorded in `logs/release-<version>.md`, which git ignores.
|
|
31
|
+
|
|
32
|
+
## Check before releasing
|
|
33
|
+
|
|
34
|
+
Releases are made from an up-to-date `main`, with nothing left uncommitted
|
|
35
|
+
except the changelog, for a version that hasn't been released yet.
|
|
36
|
+
|
|
37
|
+
```sh run
|
|
38
|
+
git switch main
|
|
39
|
+
git pull origin main
|
|
40
|
+
git fetch --tags
|
|
41
|
+
|
|
42
|
+
if [ -n "$(git status --porcelain -- . ':!CHANGELOG.md' ':!docs/CHANGELOG.md' ':!logs')" ]; then
|
|
43
|
+
echo "Commit or stash these changes first:"
|
|
44
|
+
git status --short
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
if git rev-parse --quiet --verify "refs/tags/<VERSION>" > /dev/null; then
|
|
48
|
+
echo "Tag <VERSION> already exists"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
if ! grep -qx "## <VERSION>" CHANGELOG.md; then
|
|
52
|
+
echo "CHANGELOG.md has no '## <VERSION>' section"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Run the checks
|
|
58
|
+
|
|
59
|
+
```sh run
|
|
60
|
+
make ty
|
|
61
|
+
make test
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Update the version
|
|
65
|
+
|
|
66
|
+
`pyproject.toml` holds paulrun's only copy of its version. `uv version` sets
|
|
67
|
+
it and updates `uv.lock` to match.
|
|
68
|
+
|
|
69
|
+
```sh run
|
|
70
|
+
uv version <VERSION>
|
|
71
|
+
uv run paulrun --version
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Commit and push
|
|
75
|
+
|
|
76
|
+
There's nothing to commit if the version and changelog were already
|
|
77
|
+
committed, e.g. for the first release, or when rerunning one that failed
|
|
78
|
+
after this step.
|
|
79
|
+
|
|
80
|
+
```sh run
|
|
81
|
+
if git diff --quiet HEAD; then
|
|
82
|
+
echo "Nothing to commit"
|
|
83
|
+
else
|
|
84
|
+
git commit -am "version <VERSION>"
|
|
85
|
+
fi
|
|
86
|
+
git push origin main
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Tag the release
|
|
90
|
+
|
|
91
|
+
```sh run
|
|
92
|
+
git tag <VERSION>
|
|
93
|
+
git push origin <VERSION>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Create the GitHub release
|
|
97
|
+
|
|
98
|
+
These are the release notes, from `CHANGELOG.md`:
|
|
99
|
+
|
|
100
|
+
```python run
|
|
101
|
+
import os
|
|
102
|
+
import re
|
|
103
|
+
from pathlib import Path
|
|
104
|
+
|
|
105
|
+
version = re.escape(os.environ["VERSION"])
|
|
106
|
+
changelog = Path("CHANGELOG.md").read_text()
|
|
107
|
+
notes = re.search(rf"^## {version}\n(.*?)(?=^## |\Z)", changelog, re.MULTILINE | re.DOTALL)
|
|
108
|
+
print(notes.group(1).strip())
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```confirm
|
|
112
|
+
Create a release on GitHub from tag <VERSION>:
|
|
113
|
+
https://github.com/phalt/paulrun/releases/new?tag=<VERSION>
|
|
114
|
+
|
|
115
|
+
Paste in the release notes above, and mark it as the latest release.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Publish the documentation
|
|
119
|
+
|
|
120
|
+
Takes about 5-10 minutes to deploy fully.
|
|
121
|
+
|
|
122
|
+
```sh run
|
|
123
|
+
make deploy-docs
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Release to PyPI
|
|
127
|
+
|
|
128
|
+
`uv publish` uploads everything in `dist/`, so clear it out first.
|
|
129
|
+
|
|
130
|
+
```sh run
|
|
131
|
+
rm -rf dist
|
|
132
|
+
# uv publish reads the token from $UV_PUBLISH_TOKEN.
|
|
133
|
+
make release
|
|
134
|
+
```
|