ppkit 0.1.0__tar.gz → 0.2.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.
- ppkit-0.2.0/CHANGELOG.md +234 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/CONTRIBUTING.md +92 -24
- {ppkit-0.1.0 → ppkit-0.2.0}/PKG-INFO +40 -16
- {ppkit-0.1.0 → ppkit-0.2.0}/README.md +38 -14
- {ppkit-0.1.0 → ppkit-0.2.0}/bin/pp +4 -2
- {ppkit-0.1.0 → ppkit-0.2.0}/ci/check +2 -2
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/__init__.py +1 -1
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/cli.py +41 -15
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/batch.py +299 -31
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/cmd.py +64 -42
- ppkit-0.2.0/pp/commands/completion.py +66 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/http.py +36 -12
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/kill.py +23 -3
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/promote.py +402 -77
- ppkit-0.2.0/pp/commands/queue.py +389 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/config.py +0 -13
- ppkit-0.2.0/pp/lib/jobcore.py +908 -0
- ppkit-0.2.0/pp/lib/jobpolicy.py +1164 -0
- ppkit-0.2.0/pp/lib/jobq.py +2108 -0
- ppkit-0.2.0/pp/lib/jobstore.py +486 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/paths.py +14 -8
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/ui.py +101 -8
- {ppkit-0.1.0 → ppkit-0.2.0}/pyproject.toml +2 -2
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/journey.py +10 -1
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_batch.py +200 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_cli.py +99 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_constructs.py +463 -172
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_http.py +98 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_batch.py +25 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_cli.py +103 -2
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_cmd.py +77 -1
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_http.py +2 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_promote.py +18 -0
- ppkit-0.2.0/tests/test_journey_queue.py +1402 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_kill.py +31 -0
- ppkit-0.2.0/tests/test_promote.py +1336 -0
- ppkit-0.2.0/tests/test_queue.py +5923 -0
- ppkit-0.2.0/tests/test_ui.py +319 -0
- ppkit-0.1.0/CHANGELOG.md +0 -20
- ppkit-0.1.0/pp/commands/completion.py +0 -55
- ppkit-0.1.0/pp/commands/queue.py +0 -243
- ppkit-0.1.0/pp/lib/jobq.py +0 -3106
- ppkit-0.1.0/tests/test_journey_queue.py +0 -637
- ppkit-0.1.0/tests/test_promote.py +0 -679
- ppkit-0.1.0/tests/test_queue.py +0 -3280
- ppkit-0.1.0/tests/test_ui.py +0 -107
- {ppkit-0.1.0 → ppkit-0.2.0}/.pp.toml +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/LICENSE +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/ci/package +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/__main__.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/commands/__init__.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/__init__.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/lock.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/proc.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/pp/lib/session.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/sandbox.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_cmd.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_journey_kill.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_lock.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_paths.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_proc.py +0 -0
- {ppkit-0.1.0 → ppkit-0.2.0}/tests/test_session.py +0 -0
ppkit-0.2.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.2.0
|
|
4
|
+
|
|
5
|
+
The interface this minor version keeps. `pp help queue` says what is promised
|
|
6
|
+
and what may change in a patch. `pp queue` stops what a container runner
|
|
7
|
+
started, takes a campaign from a file with one job's flags per line, and runs
|
|
8
|
+
a stage after the one it names.
|
|
9
|
+
|
|
10
|
+
`pp queue`: a job is named and selected the same way by every verb.
|
|
11
|
+
|
|
12
|
+
- A job added without `--tag` has no name. Views show its command, and a
|
|
13
|
+
selection by tag never matches it.
|
|
14
|
+
- A `--tag` is at most 64 characters of letters, digits and `.`, `_`, `-`
|
|
15
|
+
and `:`. It may not hold `..`, be `.` on its own, or read as an id or a
|
|
16
|
+
range, because a tag can be part of a log file's name.
|
|
17
|
+
- `counter` is not a queue name, beside `probes` and `watchers`: a queue of
|
|
18
|
+
that name took the place of the job id counter, and every later `add`
|
|
19
|
+
failed.
|
|
20
|
+
- Every verb that takes jobs takes the same five forms: an id, a range
|
|
21
|
+
(`12-15`), a tag matched whole, `--prefix P` (repeatable), and `--all`
|
|
22
|
+
with `-q NAME`. `--all` without `-q` is a usage error, and a tag or a
|
|
23
|
+
prefix that names jobs in two queues is an error that lists the ids.
|
|
24
|
+
`pp queue note` is the exception, and takes one job by its id or by a
|
|
25
|
+
tag that names one.
|
|
26
|
+
- `pp queue clean` takes a selection as well as `--older AGE`.
|
|
27
|
+
|
|
28
|
+
`pp queue`: a project says how its work is launched and stopped, so a kill
|
|
29
|
+
and a timeout reach work that is not in the job's process group.
|
|
30
|
+
|
|
31
|
+
- `[queue] stop` in a project's `.pp.toml`, and `--stop CMD` on `add`:
|
|
32
|
+
`"group"` where the runner's work stays in the job's own process group,
|
|
33
|
+
else a shell command that ends it. `pp queue add` is refused in a project
|
|
34
|
+
that sets `[queue] runner` and no stop, naming both spellings.
|
|
35
|
+
`--runner CMD` on `add` sets the runner for one job.
|
|
36
|
+
- In a project with a command stop, `pp queue kill` and a `--timeout` run
|
|
37
|
+
that command in place of the SIGTERM to the job's process group. The
|
|
38
|
+
grace, the SIGKILL and the sweep of the group are unchanged, so a project
|
|
39
|
+
with `stop = "group"` behaves as it did. The kill line says which ran, and
|
|
40
|
+
a stop command that failed is reported with its exit code.
|
|
41
|
+
- A job added with `--no-runner` carries `stop = "group"` whatever its
|
|
42
|
+
project says.
|
|
43
|
+
- The sweep that runs when a job's run is over also runs the project's stop
|
|
44
|
+
command, where there is one and the attempt wrote no exit code of its own,
|
|
45
|
+
and it takes the job's process group with it. So nothing the runner
|
|
46
|
+
started outlives the attempt that started it, and a leaked process no
|
|
47
|
+
longer holds a reservation the next job is told it may use.
|
|
48
|
+
- A job and its runner are given `PP_QUEUE_CONTAINER`, a name no other run
|
|
49
|
+
on the machine has, one per attempt, beginning `pp-<queue>-<id>-`; and
|
|
50
|
+
`PP_QUEUE_ENVFILE`, the path of a file of `KEY=VALUE` lines readable by
|
|
51
|
+
its owner alone. That file holds the project's `[queue] env`, the job's
|
|
52
|
+
`--env`, `CUDA_VISIBLE_DEVICES` where a card was reserved, `PP_QUEUE`,
|
|
53
|
+
`PP_QUEUE_JOB` and `PP_QUEUE_CONTAINER`, and nothing else of the
|
|
54
|
+
machine's: no `PATH`, `VIRTUAL_ENV`, `CONDA_PREFIX`, `CONDA_DEFAULT_ENV`
|
|
55
|
+
or `PP_QUEUE_RCFILE`. The record names it, and it goes with the record.
|
|
56
|
+
- An `--env` value, or a `[queue] env` value, holding a newline is refused,
|
|
57
|
+
and so is a name holding `=`, NUL or a newline.
|
|
58
|
+
- An exit code an attempt wrote while a stop of it was going out is that
|
|
59
|
+
stop's outcome, killed or timeout, with the code kept as `rc`.
|
|
60
|
+
- `pp queue kill --grace DUR` takes a duration and overrides the job's own
|
|
61
|
+
grace for the jobs that kill names.
|
|
62
|
+
|
|
63
|
+
`pp queue`: a job's output goes where the project says.
|
|
64
|
+
|
|
65
|
+
- `log` in a project's `[queue]`, and `--log PATH` on `add`: a path with
|
|
66
|
+
`{tag}` or `{id}` in it, relative to the project root or absolute. Any
|
|
67
|
+
other placeholder, a path holding `..`, and a relative path that leaves
|
|
68
|
+
the project root are refused. `{tag}` on a job with no tag is filled in
|
|
69
|
+
with the job's id.
|
|
70
|
+
- Every attempt appends to that one file, after a `[pp queue]` line naming
|
|
71
|
+
the job and its command, and several jobs may name one file. The record
|
|
72
|
+
names the file; `log`, `show`, a failure's tail and `PP_QUEUE_LOG` all
|
|
73
|
+
read it from there. `drop` and `clean` remove the files pp put in its own
|
|
74
|
+
store, and never a log at a path you named.
|
|
75
|
+
- `--retry-if` matches from the byte offset where the failed attempt's own
|
|
76
|
+
output begins, so it no longer matches an earlier attempt's lines or pp's
|
|
77
|
+
own line.
|
|
78
|
+
|
|
79
|
+
`pp queue`: a campaign file carries one job's flags per line, and a job can
|
|
80
|
+
wait for the jobs it names.
|
|
81
|
+
|
|
82
|
+
- A line of a `--from` file is the rest of an `add` command line: its own
|
|
83
|
+
flags, then `--`, then the command. A flag on the `add` is the default for
|
|
84
|
+
every line and a line's own flag wins over it; `--env` and `--need` merge
|
|
85
|
+
key by key. A line that is only a command behaves as before.
|
|
86
|
+
- A line is split into words the way a shell splits them, with quotes and
|
|
87
|
+
backslashes honoured. A line holding `|`, `&`, `;`, `<`, `>`, `(`, `)`,
|
|
88
|
+
`` ` `` or `$(` outside quotes is refused with its line number, and so is a
|
|
89
|
+
line with no `--` whose first word begins with `-`. A file one line of
|
|
90
|
+
which is refused queues nothing. A command that wants a shell operator
|
|
91
|
+
passes it quoted: `-- sh -c 'python x.py | grep -v noise'`.
|
|
92
|
+
- `--from FILE` together with a command on the same command line is a usage
|
|
93
|
+
error, and so is a line that names the queue, the file, `--hold` or
|
|
94
|
+
`--no-start`, which are the batch's and no line's.
|
|
95
|
+
- `--after SELECTION` on `add`: the job waits until every job that selection
|
|
96
|
+
named has ended, whatever the outcome. The value is one or more selections
|
|
97
|
+
separated by commas (`41,42`, `41-66`, `s1_eval,s1_report`), read as the
|
|
98
|
+
job is added and kept as ids on the record. An id no job has is a usage
|
|
99
|
+
error and nothing is queued. A job that is waiting is skipped rather than
|
|
100
|
+
waited for, so it never becomes the head of its queue, and a job whose
|
|
101
|
+
record is gone counts as ended.
|
|
102
|
+
- `pp queue retry` over a range, a tag, a prefix or `--all` shows a plan and
|
|
103
|
+
asks, as `kill` and `drop` do; `-n` shows the plan and `-y` does not ask.
|
|
104
|
+
A job that succeeded has ended, so it suits `retry`. The new job is owned
|
|
105
|
+
by the session that asked for it, and a priority is set with
|
|
106
|
+
`pp queue reprio` on the job the retry queued.
|
|
107
|
+
- A bare `pp queue wait` waits for every job that is queued or running
|
|
108
|
+
instead of returning at the first outcome. It names no held job; a
|
|
109
|
+
selection that names one waits for it and says so. `--timeout` is honoured
|
|
110
|
+
whatever `--poll` is, and ids together with `--idle` or `--mine` is a
|
|
111
|
+
usage error.
|
|
112
|
+
- A worker that adopts a running job takes the offset its output begins at
|
|
113
|
+
from the job's own run claim, so `--retry-if` reads that attempt's lines
|
|
114
|
+
and never pp's own line.
|
|
115
|
+
|
|
116
|
+
`pp queue`: every setting has one declaration, one default and one order of
|
|
117
|
+
its sources.
|
|
118
|
+
|
|
119
|
+
- Each setting of a job is a key of a project's `.pp.toml [queue]` as well
|
|
120
|
+
as of `[queue.NAME]`: `timeout`, `retries`, `retry_delay`, `retry_if`,
|
|
121
|
+
`on_fail`, `on_done`, `prio`, `gpu_gb`, `cpu`, `mem_gb`, `need`, `env`,
|
|
122
|
+
`log` and `grace`. The first source that gives a setting wins, in this
|
|
123
|
+
order: `add`'s flag, the project's `[queue]`, the queue's `[queue.NAME]`,
|
|
124
|
+
the built-in default. `env` and `need` merge key by key.
|
|
125
|
+
- `grace` is one setting of the job: `--grace DUR` on `add`, the `grace`
|
|
126
|
+
key, the resolved value on the record, and 10s by default. A timeout's
|
|
127
|
+
stop and a kill of that job both take it.
|
|
128
|
+
- Durations are read one way everywhere: `90s`, `30m`, `2h`, `1d`, a bare
|
|
129
|
+
number of seconds, `.5` and `1.5h`, and `none` where a setting offers it.
|
|
130
|
+
An empty value, a value that is not finite and one over 1e9 seconds are
|
|
131
|
+
refused, in a config key as on a command line.
|
|
132
|
+
- Numbers are bounded: `retries` at most 1000, `retry_delay` at most the
|
|
133
|
+
one-hour cap, a queue's `poll` longer than 0. A need that is 0, negative
|
|
134
|
+
or nan is refused.
|
|
135
|
+
- `pp queue work` takes no `--backfill` and no `--poll`: both are keys of
|
|
136
|
+
`[queue.NAME]`, which the queue's worker re-reads every loop, so they
|
|
137
|
+
change under running jobs with no restart.
|
|
138
|
+
|
|
139
|
+
`pp queue`: the store and the machine surface.
|
|
140
|
+
|
|
141
|
+
- A queue's `FORMAT` is 2. A pp that reads another format refuses the queue
|
|
142
|
+
where it opens it, naming the format the queue says and the one that pp
|
|
143
|
+
reads, rather than reading it.
|
|
144
|
+
- The `--json` field `gpu`, an integer, becomes `cards`, a list of card
|
|
145
|
+
indices; a job that reserved no card has `cards: []`.
|
|
146
|
+
- New `--json` fields: `stop_cmd`, `grace`, `after`, `container`, `envfile`,
|
|
147
|
+
`log_at`, and `cards`, `container`, `envfile` and `log_at` in each
|
|
148
|
+
`history` entry. `show --json` carries every promised field on a job in
|
|
149
|
+
each of the five states: a field the record does not hold yet is null, or
|
|
150
|
+
an empty list. The two exceptions are `after`, which is there only on a
|
|
151
|
+
job that names the jobs it waits for, and `stop`, only on a job asked to
|
|
152
|
+
stop.
|
|
153
|
+
- `pp help queue` is one page: a first job, a project whose work runs in a
|
|
154
|
+
container in four steps, names and selection, campaign files, the settings
|
|
155
|
+
table, how a job ends, workers, and what is left to you.
|
|
156
|
+
|
|
157
|
+
`pp kill`: `--grace DUR` takes a duration in the spelling every duration pp
|
|
158
|
+
takes has. A bare number is still seconds.
|
|
159
|
+
|
|
160
|
+
`pp promote`, `pp cmd` and `pp http`: plans under `-n`, help by flag, and
|
|
161
|
+
links one target holds.
|
|
162
|
+
|
|
163
|
+
- `pp -h NAME` and `pp --help NAME` print that command's help, as
|
|
164
|
+
`pp help NAME` does. `pp -h NAME VERB` prints the verb's.
|
|
165
|
+
- `pp promote rollback`, `pp promote link`, `pp promote init` and
|
|
166
|
+
`pp cmd new` take `-n`: each one shows its plan and changes nothing.
|
|
167
|
+
- `pp promote` does not take over a link path another target holds: the
|
|
168
|
+
refusal names that target, and `--force-links` moves the link.
|
|
169
|
+
- `pp promote status --json` has `failed`: the current release's
|
|
170
|
+
activation failed, or nothing gated it.
|
|
171
|
+
- `pp promote diff --json` has `source_gone`. A target whose project
|
|
172
|
+
directory is gone exits 1 there, in place of git's error.
|
|
173
|
+
- `pp cmd ls --json` has `aliases`, each command's short names.
|
|
174
|
+
- An unknown key in `[[http.roots]]` or `[[http.discover]]` is a config
|
|
175
|
+
error naming the file and the key, so a misspelt `marker` stops the
|
|
176
|
+
server instead of serving every folder under `under`.
|
|
177
|
+
- `pp help http` documents every `[http]` key with its default.
|
|
178
|
+
|
|
179
|
+
## v0.1.1
|
|
180
|
+
|
|
181
|
+
Fixes. A few command lines and config values that did harm are refused.
|
|
182
|
+
|
|
183
|
+
- `pp batch run` refuses a file-name field where a shell would read the
|
|
184
|
+
name again as code: inside `$( )`, backticks, `${ }`, arithmetic, a
|
|
185
|
+
`[[ ]]` test, a case pattern, a comment, `eval`, `trap`, a shell's `-c`
|
|
186
|
+
script, or a template with a heredoc or a function. The message names the
|
|
187
|
+
spelling that works: `sh -c 'cmd "$1"' sh {}`. Its plan lists every
|
|
188
|
+
command.
|
|
189
|
+
- A `pp batch rename` that is interrupted leaves a journal of what landed,
|
|
190
|
+
and `pp batch undo` takes that journal and says so.
|
|
191
|
+
- `pp promote WORD` releases nothing when the word is neither a verb nor a
|
|
192
|
+
path. It used to release the project in the current directory.
|
|
193
|
+
- A wrong value in `[promote]` is refused, naming the file and the key.
|
|
194
|
+
`name` must be one path component: `name = ".."` let `gc` remove every
|
|
195
|
+
release and plugin.
|
|
196
|
+
- `pp promote` keeps the release it flips away from, so the one you rolled
|
|
197
|
+
back to is still there after the next promote.
|
|
198
|
+
- A socket or fifo in a tree project no longer hangs `pp promote`, and a
|
|
199
|
+
file pp cannot read is a blocker that offers `exclude`.
|
|
200
|
+
- A hook that leaves a process running no longer holds `pp promote`. Each
|
|
201
|
+
hook's output is a file beside the run's log.
|
|
202
|
+
- A release that `pp promote gc` cannot remove stays whole, and gc exits 1.
|
|
203
|
+
- `pp queue`: a job that cannot start fails on its own and the rest start.
|
|
204
|
+
A job whose wrapper is still ending reads running, not lost.
|
|
205
|
+
- `pp queue --json` and `pp queue show --json` give a job's variables by
|
|
206
|
+
name, with each value `***`.
|
|
207
|
+
- An empty duration such as `--timeout ""` is a usage error, and so is an
|
|
208
|
+
empty `pp kill` target, which selected every process you own.
|
|
209
|
+
- `pp http` never serves `.git`, serves a file whose name is not UTF-8, and
|
|
210
|
+
sorts by name when `?sort=` names anything else.
|
|
211
|
+
- pp's messages and the `pp cmd add` and `update` screens escape the text
|
|
212
|
+
they name. TAB completion runs nothing in a command's name: run
|
|
213
|
+
`pp completion bash` or `zsh` again to replace a saved script.
|
|
214
|
+
- A command file whose name is not UTF-8 no longer breaks `pp -h`.
|
|
215
|
+
- pp needs Python 3.11.4 or later, which `pp promote` always needed.
|
|
216
|
+
|
|
217
|
+
## v0.1.0
|
|
218
|
+
|
|
219
|
+
The first release.
|
|
220
|
+
|
|
221
|
+
- `pp queue` runs commands later, in named queues, when the GPU memory,
|
|
222
|
+
CPUs, RAM or counters they ask for are free. Jobs have timeouts, retries
|
|
223
|
+
for the failures you name, and hooks. Tags and ranges select groups of
|
|
224
|
+
jobs. A project's `.pp.toml` can run its jobs through a runner, such as a
|
|
225
|
+
container.
|
|
226
|
+
- `pp queue wait`, `watch` and `wait --mine` tell a person or an agent how
|
|
227
|
+
jobs ended, with the end of the log for a failure.
|
|
228
|
+
- `pp promote` releases a project from a git commit when its check passes
|
|
229
|
+
on that copy, and rolls back.
|
|
230
|
+
- `pp kill`, `pp batch` and `pp http` stop processes, rename or convert
|
|
231
|
+
files, and show project folders in a browser.
|
|
232
|
+
- `pp cmd` writes commands, and adds other people's from git, pinned to the
|
|
233
|
+
commit you read.
|
|
234
|
+
- Completion for zsh and bash.
|
|
@@ -28,19 +28,19 @@ If you installed pp as a tool, `uv tool uninstall ppkit` (or
|
|
|
28
28
|
|
|
29
29
|
## Building and testing
|
|
30
30
|
|
|
31
|
-
You need Python 3.11 or later, git, sh and bash. The tests that need zsh
|
|
31
|
+
You need Python 3.11.4 or later, git, sh and bash. The tests that need zsh
|
|
32
32
|
skip without it. `ci/check` uses [uv](https://docs.astral.sh/uv/)
|
|
33
33
|
for the linter and for other Python versions.
|
|
34
34
|
|
|
35
35
|
```
|
|
36
36
|
ci/check the linter, then every test, on python3
|
|
37
|
-
ci/check 3.11
|
|
38
|
-
ci/check --cpus 2 3.11
|
|
37
|
+
ci/check 3.11.4 the same on Python 3.11.4, which uv installs the first time
|
|
38
|
+
ci/check --cpus 2 3.11.4 the same on 2 CPUs, like a CI runner
|
|
39
39
|
python3 -m unittest discover -s tests the tests alone
|
|
40
40
|
ci/package build ppkit into dist/, install it fresh, and try it
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
CI runs `ci/check 3.11`, `ci/check 3.14` and `ci/package` on every push to
|
|
43
|
+
CI runs `ci/check 3.11.4`, `ci/check 3.14` and `ci/package` on every push to
|
|
44
44
|
main and every pull request. The whole suite takes a few minutes. Most of
|
|
45
45
|
that is the journeys (`tests/test_journey_*.py`), which run the real
|
|
46
46
|
`bin/pp` once per step in a throwaway home directory.
|
|
@@ -55,7 +55,7 @@ on a developer's machine takes tens of milliseconds, and a runner's few
|
|
|
55
55
|
hundred take a few. The test that needs another user's process is skipped
|
|
56
56
|
there.
|
|
57
57
|
|
|
58
|
-
A uv-managed CPython has no `os.pidfd_open`, so `ci/check 3.11`, `ci/check
|
|
58
|
+
A uv-managed CPython has no `os.pidfd_open`, so `ci/check 3.11.4`, `ci/check
|
|
59
59
|
3.14` and CI run `pp/lib/proc.py`'s ctypes syscall path, while the distro
|
|
60
60
|
`python3` of a plain `ci/check` runs the wrapped calls. Both are tested only
|
|
61
61
|
by running both.
|
|
@@ -149,6 +149,13 @@ These rules keep pp working as commands come and go:
|
|
|
149
149
|
command, two workers, a signal and a write) go through a construct that
|
|
150
150
|
is written once. "The constructs" below is the list of them, and a check
|
|
151
151
|
added at one call site is not a fix.
|
|
152
|
+
- A test checks a contract, not a wording. Every sentence pp prints may be
|
|
153
|
+
reworded in a patch, so a test asserts an exit status, a `--json` field, a
|
|
154
|
+
record field, a file's existence or its mode, an id, or a state, and nothing
|
|
155
|
+
else. Where a test has to know that pp said something, it asserts the field
|
|
156
|
+
the sentence was made from. Two exceptions are written down where they are
|
|
157
|
+
made: a journey types the examples of `pp help NAME`, and one journey step
|
|
158
|
+
holds the headings and the line width of `pp help queue`.
|
|
152
159
|
- A test of a construct checks what the construct promises. A test that
|
|
153
160
|
runs a second actor at an exact step keeps one fix from coming back
|
|
154
161
|
(`TestTwoAtOnce` in `tests/test_queue.py`). That step goes through
|
|
@@ -185,8 +192,11 @@ list of the parts:
|
|
|
185
192
|
|
|
186
193
|
- `pp/cli.py`: finding commands, the order of their origins, help and
|
|
187
194
|
completion.
|
|
188
|
-
- `pp/lib/
|
|
189
|
-
|
|
195
|
+
- `pp/lib/jobstore.py`: a job's record, the files it lives in, and the
|
|
196
|
+
transaction that changes one. `pp/lib/jobcore.py`: the worker, the claims
|
|
197
|
+
and the processes of a queue. `pp/lib/jobpolicy.py`: every rule of a queue
|
|
198
|
+
as a value, so each one is a table. `pp/lib/jobq.py`: the verbs, the views,
|
|
199
|
+
the resources and the CLI; `pp help queue` is the user's view of the same.
|
|
190
200
|
- `pp/commands/promote.py`: releases. `pp/commands/cmd.py`: plugins.
|
|
191
201
|
`pp/commands/http.py`: serving a folder to a browser, with its cache and
|
|
192
202
|
its server. `pp/commands/batch.py`: bulk file changes, and the journal
|
|
@@ -214,9 +224,9 @@ this table, and each rule's own docstring is its contract.
|
|
|
214
224
|
| `alive` | judging a process alive by its pid | a claim's holder, or `Handle` | `pp/lib/proc.py` |
|
|
215
225
|
| `lock` | taking a lock | `locked`, and `deferring` for a scope with no lock | `pp/lib/lock.py` |
|
|
216
226
|
| `claim` | opening a claim's file | `Claim` | `pp/lib/lock.py` |
|
|
217
|
-
| `job` | reading or writing a job's file | `txn`, `listing`, `find` | `pp/lib/
|
|
218
|
-
| `worker-claim` | taking a queue's worker claim, or reading its holder | `worker`, `worker_now`, `work` | `pp/lib/
|
|
219
|
-
| `job-process` | signalling or reaping a job's processes | the `Worker` class, `
|
|
227
|
+
| `job` | reading or writing a job's file | `txn`, `listing`, `find` | `pp/lib/jobstore.py` |
|
|
228
|
+
| `worker-claim` | taking a queue's worker claim, or reading its holder | `worker`, `worker_now`, `work` | `pp/lib/jobcore.py` |
|
|
229
|
+
| `job-process` | signalling or reaping a job's processes | the `Worker` class, `worker_stop`, `work` | `pp/lib/jobcore.py`, `pp/lib/proc.py`, `pp/commands/kill.py` |
|
|
220
230
|
| `by-name` | deciding on a served file by its name | act on the open file | `pp/commands/http.py` |
|
|
221
231
|
| `sleep` | sleeping in a test | `until` | `tests/journey.py` |
|
|
222
232
|
| `once` | a once-flag of a test's own | `Gate` | `tests/journey.py` |
|
|
@@ -246,15 +256,66 @@ replacing a whole file in one step goes through `paths.write_json`.
|
|
|
246
256
|
`tests/test_cmd.py` and `ci/package` keep the installed case working, and
|
|
247
257
|
the journeys in `tests/test_journey_cmd.py` the other two.
|
|
248
258
|
|
|
259
|
+
### The queue's four modules, and what may go where
|
|
260
|
+
|
|
261
|
+
The line between them is mechanism against policy, and a change goes to the
|
|
262
|
+
module whose job it is:
|
|
263
|
+
|
|
264
|
+
- `pp/lib/jobstore.py` is the record store and its transaction: one lock, no
|
|
265
|
+
timing, no process, no signal. A field a record gains and a write a verb
|
|
266
|
+
hands over go here.
|
|
267
|
+
- `pp/lib/jobcore.py` is the worker and the claims: starting, stopping,
|
|
268
|
+
reaping and adopting a job's processes, the worker's loop, and the watchers.
|
|
269
|
+
A bug here loses a job, which is why it is the module held still.
|
|
270
|
+
- `pp/lib/jobpolicy.py` is every rule that is values in and one value out: how
|
|
271
|
+
a job ends, what a selection names, what a setting comes to, what a job's
|
|
272
|
+
environment is. A rule goes here, and a table tests it.
|
|
273
|
+
- `pp/lib/jobq.py` is the verbs, the views, the readings, the one settings
|
|
274
|
+
declaration and the CLI, and `pp/commands/queue.py` is the command and its
|
|
275
|
+
help. Every sentence a user reads is worded here.
|
|
276
|
+
|
|
277
|
+
The store and the worker together are the core. These rules hold that line,
|
|
278
|
+
and `tests/test_constructs.py` reads the source for each:
|
|
279
|
+
|
|
280
|
+
- The boundary rule. Outside the core no function takes the store lock, and
|
|
281
|
+
none starts a job, a runner, a stop command, a hook or a worker, or signals
|
|
282
|
+
a job, a worker or a process group: a verb goes through the core's API, where
|
|
283
|
+
every write is one call and one whole transaction. Two sites outside it run a
|
|
284
|
+
process of their own, and both are named exemptions: `reading`, which runs a
|
|
285
|
+
user's probe under that probe's own claim, and `_cards`, which runs
|
|
286
|
+
nvidia-smi.
|
|
287
|
+
- The purity rule. A function of `jobpolicy.py` reaches no clock, no store, no
|
|
288
|
+
process, no signal and no file. It is handed records, readings, settings and
|
|
289
|
+
`now`, and gives back a value, so a rule that could be raced is a rule no
|
|
290
|
+
table tests.
|
|
291
|
+
- The import rule. `jobpolicy.py` imports nothing of pp's, the store imports
|
|
292
|
+
nothing of the worker, and neither imports the verbs above them. The
|
|
293
|
+
direction is a line, so no cycle can hide a rule that reads a mechanism.
|
|
294
|
+
- The core reads no config file and takes no reading of its own: `read_settings`
|
|
295
|
+
hands a worker the settings of one pass of its loop, and `admit_for` the
|
|
296
|
+
readings of one launch pass. It writes no sentence either: it tells a sink
|
|
297
|
+
what it did, and `jobq` words every one of them.
|
|
298
|
+
- The budgets, in code lines that are neither blank, comment nor docstring:
|
|
299
|
+
250 for the store, 490 for the worker, 730 for the two together. A feature
|
|
300
|
+
that cannot be built without going over one of them waits until something
|
|
301
|
+
leaves the core, and moving a rule out into `jobpolicy.py` is the first thing
|
|
302
|
+
to try.
|
|
303
|
+
- Every function of the store and of the worker is named in one of the two
|
|
304
|
+
lists in that file, so a construct is placed when it lands, and a name that
|
|
305
|
+
has gone comes off the list in the same change.
|
|
306
|
+
|
|
249
307
|
### Where a queue's jobs live, and how one says it is alive
|
|
250
308
|
|
|
251
|
-
The docstring of `pp/lib/
|
|
252
|
-
of, how a worker and a job say they are
|
|
253
|
-
job. Each mechanism
|
|
309
|
+
The docstring of `pp/lib/jobstore.py` is the reference for the files a queue
|
|
310
|
+
is made of, and `pp/lib/jobcore.py`'s for how a worker and a job say they are
|
|
311
|
+
alive and which actor ends a job. Each mechanism has one owner, and these are
|
|
312
|
+
they:
|
|
254
313
|
|
|
255
|
-
- the store: `Txn`, `txn`, `listing` and `find
|
|
256
|
-
queue's `FORMAT` is checked, and `Txn.add` is where
|
|
257
|
-
that holds it land together.
|
|
314
|
+
- the store: `Txn`, `txn`, `listing` and `find`, in `pp/lib/jobstore.py`.
|
|
315
|
+
`open_queue` is where a queue's `FORMAT` is checked, and `Txn.add` is where
|
|
316
|
+
an id and the record that holds it land together. The writes a verb hands
|
|
317
|
+
over are the four at the end of that file, each one a whole transaction of
|
|
318
|
+
its own that is told the states it may act on.
|
|
258
319
|
- being alive: `lock.Claim`. A claim is held for as long as its holder lives,
|
|
259
320
|
so nothing is cleaned up after a holder that was killed and no record is
|
|
260
321
|
ever read as a live holder. A queue's worker holds one on `worker.lock`,
|
|
@@ -262,9 +323,13 @@ job. Each mechanism inside it has one owner, and these are they:
|
|
|
262
323
|
- whether a queue's worker is up: the block comment above `worker_up`, for
|
|
263
324
|
the two things that can be true of that claim and the first moment that is
|
|
264
325
|
neither.
|
|
265
|
-
- how a job ends: `decide` and `merge_stop`, which
|
|
266
|
-
|
|
267
|
-
action out.
|
|
326
|
+
- how a job ends: `decide` and `merge_stop` in `pp/lib/jobpolicy.py`, which
|
|
327
|
+
are pure, as every rule in that module is, and `Worker.carry_out`, the one
|
|
328
|
+
pass that gathers the facts and carries the action out.
|
|
329
|
+
- what a worker is handed: it reads no config file and takes no reading of
|
|
330
|
+
its own, so `read_settings` gives it the settings of one pass of its loop
|
|
331
|
+
and `admit_for` the readings of one launch pass. It writes no sentence
|
|
332
|
+
either: it tells a sink what it did, and `jobq` words every one of them.
|
|
268
333
|
- what a stop carries: `public()`, where every field of a job record that
|
|
269
334
|
others rely on is named.
|
|
270
335
|
|
|
@@ -281,9 +346,10 @@ the same place.
|
|
|
281
346
|
A kill, pass by pass:
|
|
282
347
|
|
|
283
348
|
```
|
|
284
|
-
kill:
|
|
285
|
-
worker: carry_out: the facts -> decide = TERM ->
|
|
286
|
-
|
|
349
|
+
kill: ask_stop(merge_stop) -> worker_up(wake=True) -> SIGUSR1 to the worker
|
|
350
|
+
worker: carry_out: the facts -> decide = TERM -> the project's stop command,
|
|
351
|
+
or the SIGTERM to the group, then txn marks `sent`
|
|
352
|
+
-> (the grace) -> decide = KILL9 -> signal
|
|
287
353
|
-> the wrapper ends, so the job's run claim is free
|
|
288
354
|
-> decide = ENDED_AS -> _finish records the outcome and the hook it owes
|
|
289
355
|
-> fire
|
|
@@ -297,9 +363,11 @@ own exit time and not the queue's poll period.
|
|
|
297
363
|
|
|
298
364
|
The rules the worker follows are the ones `pp help queue` states under "How a
|
|
299
365
|
job ends, whatever reached it", and that help text is their one statement.
|
|
300
|
-
|
|
366
|
+
Two more are for contributors: which of the two a stop is comes from the
|
|
301
367
|
`timeout` field a worker writes on the stop it wrote itself, never from the
|
|
302
|
-
name of whoever asked
|
|
368
|
+
name of whoever asked; and an exit code the attempt wrote after its stop went
|
|
369
|
+
out is that stop's outcome, with the code kept, since a container runner writes
|
|
370
|
+
the code of a container the stop command ended.
|
|
303
371
|
|
|
304
372
|
`TestTheDecision` in `tests/test_queue.py` is a table over every combination
|
|
305
373
|
of those facts. Each row carries the rule from that help as its own label,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ppkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A CLI kit for user & agents
|
|
5
5
|
Keywords: cli,job queue,gpu,agents,toolkit
|
|
6
6
|
Author: The pp Authors
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
15
|
Classifier: Topic :: System :: Shells
|
|
16
16
|
Classifier: Topic :: Utilities
|
|
17
|
-
Requires-Python: >=3.11
|
|
17
|
+
Requires-Python: >=3.11.4
|
|
18
18
|
Project-URL: Changelog, https://github.com/xflash96/pp/blob/main/CHANGELOG.md
|
|
19
19
|
Project-URL: Homepage, https://github.com/xflash96/pp
|
|
20
20
|
Project-URL: Issues, https://github.com/xflash96/pp/issues
|
|
@@ -85,7 +85,7 @@ shell that added it.
|
|
|
85
85
|
|
|
86
86
|
## Install
|
|
87
87
|
|
|
88
|
-
pp runs on Linux, with Python 3.11 or later. Install it as a tool, in an
|
|
88
|
+
pp runs on Linux, with Python 3.11.4 or later. Install it as a tool, in an
|
|
89
89
|
environment of its own:
|
|
90
90
|
|
|
91
91
|
```
|
|
@@ -132,8 +132,8 @@ pp queue add -q dl --need net=1 -- ./fetch.sh the dl queue, taking on
|
|
|
132
132
|
pp queue add --timeout 2h -- python x.py stopped after two hours
|
|
133
133
|
pp queue add --retries 2 --retry-if 'Connection reset' -- ./sync.sh
|
|
134
134
|
pp queue add --on-fail 'notify-send "job $PP_QUEUE_JOB failed"' -- ./nightly.sh
|
|
135
|
-
pp queue add --tag sweep -- python x.py --lr 0.1
|
|
136
|
-
pp queue wait sweep
|
|
135
|
+
pp queue add --tag sweep.lr1 -- python x.py --lr 0.1 a name for the job, and for its family
|
|
136
|
+
pp queue wait --prefix sweep. every job whose name starts with sweep.
|
|
137
137
|
pp queue watch 12 a line for each start, retry and outcome
|
|
138
138
|
pp queue drop 20-29 a range: it shows the jobs and asks first
|
|
139
139
|
pp queue kill 12 waits while the worker stops it, then:
|
|
@@ -141,6 +141,11 @@ pp queue kill 12 waits while the worker
|
|
|
141
141
|
pp queue retry 11 pp queue hold 15 pp queue release 15
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
Every verb that takes jobs takes the same five forms: an id, a range of ids,
|
|
145
|
+
a `--tag` name matched whole, `--prefix P`, and `--all` with `-q NAME`. `pp
|
|
146
|
+
queue note` is the exception, and takes one job by its id or by a tag that
|
|
147
|
+
names one. A job added without `--tag` has no name, and its id selects it.
|
|
148
|
+
|
|
144
149
|
Limits go in `~/.config/pp/config.toml`. Every queue draws on the shared
|
|
145
150
|
GPU memory, CPUs and RAM; a queue's own pool caps what it may take of them.
|
|
146
151
|
A counter is a number, or a command that prints how much is free:
|
|
@@ -158,21 +163,36 @@ retry_if = "Connection reset|503"
|
|
|
158
163
|
net = 2
|
|
159
164
|
```
|
|
160
165
|
|
|
161
|
-
A project's `.pp.toml`
|
|
162
|
-
|
|
166
|
+
A project's `.pp.toml` says how its jobs are launched and stopped, where
|
|
167
|
+
their output goes, and what is in their environment:
|
|
163
168
|
|
|
164
169
|
```toml
|
|
165
170
|
[queue]
|
|
166
|
-
runner = "docker/run.sh"
|
|
171
|
+
runner = "docker/run.sh" # the job runs as docker/run.sh CMD...
|
|
172
|
+
stop = "docker rm -f $PP_QUEUE_CONTAINER" # how a stop reaches the work the runner started
|
|
173
|
+
log = "logs/{tag}.log" # under the project root; {tag} or {id}
|
|
167
174
|
env = { HF_HUB_OFFLINE = "1" }
|
|
168
175
|
```
|
|
169
176
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
A runner puts the work where no signal of pp's reaches it, so a project that
|
|
178
|
+
sets one says the stop as well, or the `add` is refused. pp hands the runner
|
|
179
|
+
`PP_QUEUE_CONTAINER`, a name for whatever it starts elsewhere, and
|
|
180
|
+
`PP_QUEUE_ENVFILE`, a file of `KEY=VALUE` lines for a container to read with
|
|
181
|
+
`--env-file`. `pp queue add --no-runner` skips the runner for one job, and
|
|
182
|
+
`--runner CMD` with `--stop CMD` gives one job both with no `.pp.toml`.
|
|
183
|
+
|
|
184
|
+
A campaign is a file of one job per line, each line with its own flags, and a
|
|
185
|
+
stage can wait for the one before it:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
IDS=$(pp queue add --from stage1.txt) # one id per line on stdout
|
|
189
|
+
pp queue add --from stage2.txt --after "$(echo "$IDS" | tr '\n' ',')"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Give a job a token or a secret with `--env` or the project's `env`: a variable
|
|
193
|
+
exported in your shell reaches it only through the worker. `--json` gives a
|
|
194
|
+
job's variables by name, with each value `***`. `pp help queue` is the whole
|
|
195
|
+
page, with the runner to copy, and `pp help queue add` every option of `add`.
|
|
176
196
|
|
|
177
197
|
## Working with agents
|
|
178
198
|
|
|
@@ -231,6 +251,10 @@ pp promote rollback notes back to the release before
|
|
|
231
251
|
database migration that cannot be undone does not belong in it. pp keeps the
|
|
232
252
|
last five releases, and always keeps the current one and the one before.
|
|
233
253
|
|
|
254
|
+
Each hook's output goes to a file beside the run's log, and pp shows it as it
|
|
255
|
+
comes. pp waits for the hook itself, not for what the hook leaves running, so
|
|
256
|
+
redirect the output of a process an `after` starts.
|
|
257
|
+
|
|
234
258
|
A systemd service runs the release by pointing at `current`:
|
|
235
259
|
|
|
236
260
|
```
|
|
@@ -262,7 +286,7 @@ pp http ~/notes . a tab for each folder, at http://127.0.0.1:8000
|
|
|
262
286
|
```
|
|
263
287
|
|
|
264
288
|
`pp http` listens on 127.0.0.1 unless you pass `--bind`, and does not serve
|
|
265
|
-
files such as `.env` or private keys.
|
|
289
|
+
`.git` or files such as `.env` or private keys.
|
|
266
290
|
|
|
267
291
|
## Your own commands
|
|
268
292
|
|
|
@@ -314,7 +338,7 @@ too. To add a command to pp itself, see [CONTRIBUTING.md](https://github.com/xfl
|
|
|
314
338
|
pp is new, and its version is 0.x. A command or a config key may still
|
|
315
339
|
change, and so may a field of `--json` output; the
|
|
316
340
|
[changelog](https://github.com/xflash96/pp/blob/main/CHANGELOG.md) says when one does. CI runs the tests on
|
|
317
|
-
Python 3.11 and 3.14, on Ubuntu 24.04.
|
|
341
|
+
Python 3.11.4 and 3.14, on Ubuntu 24.04.
|
|
318
342
|
|
|
319
343
|
## Contributing
|
|
320
344
|
|
|
@@ -63,7 +63,7 @@ shell that added it.
|
|
|
63
63
|
|
|
64
64
|
## Install
|
|
65
65
|
|
|
66
|
-
pp runs on Linux, with Python 3.11 or later. Install it as a tool, in an
|
|
66
|
+
pp runs on Linux, with Python 3.11.4 or later. Install it as a tool, in an
|
|
67
67
|
environment of its own:
|
|
68
68
|
|
|
69
69
|
```
|
|
@@ -110,8 +110,8 @@ pp queue add -q dl --need net=1 -- ./fetch.sh the dl queue, taking on
|
|
|
110
110
|
pp queue add --timeout 2h -- python x.py stopped after two hours
|
|
111
111
|
pp queue add --retries 2 --retry-if 'Connection reset' -- ./sync.sh
|
|
112
112
|
pp queue add --on-fail 'notify-send "job $PP_QUEUE_JOB failed"' -- ./nightly.sh
|
|
113
|
-
pp queue add --tag sweep -- python x.py --lr 0.1
|
|
114
|
-
pp queue wait sweep
|
|
113
|
+
pp queue add --tag sweep.lr1 -- python x.py --lr 0.1 a name for the job, and for its family
|
|
114
|
+
pp queue wait --prefix sweep. every job whose name starts with sweep.
|
|
115
115
|
pp queue watch 12 a line for each start, retry and outcome
|
|
116
116
|
pp queue drop 20-29 a range: it shows the jobs and asks first
|
|
117
117
|
pp queue kill 12 waits while the worker stops it, then:
|
|
@@ -119,6 +119,11 @@ pp queue kill 12 waits while the worker
|
|
|
119
119
|
pp queue retry 11 pp queue hold 15 pp queue release 15
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
Every verb that takes jobs takes the same five forms: an id, a range of ids,
|
|
123
|
+
a `--tag` name matched whole, `--prefix P`, and `--all` with `-q NAME`. `pp
|
|
124
|
+
queue note` is the exception, and takes one job by its id or by a tag that
|
|
125
|
+
names one. A job added without `--tag` has no name, and its id selects it.
|
|
126
|
+
|
|
122
127
|
Limits go in `~/.config/pp/config.toml`. Every queue draws on the shared
|
|
123
128
|
GPU memory, CPUs and RAM; a queue's own pool caps what it may take of them.
|
|
124
129
|
A counter is a number, or a command that prints how much is free:
|
|
@@ -136,21 +141,36 @@ retry_if = "Connection reset|503"
|
|
|
136
141
|
net = 2
|
|
137
142
|
```
|
|
138
143
|
|
|
139
|
-
A project's `.pp.toml`
|
|
140
|
-
|
|
144
|
+
A project's `.pp.toml` says how its jobs are launched and stopped, where
|
|
145
|
+
their output goes, and what is in their environment:
|
|
141
146
|
|
|
142
147
|
```toml
|
|
143
148
|
[queue]
|
|
144
|
-
runner = "docker/run.sh"
|
|
149
|
+
runner = "docker/run.sh" # the job runs as docker/run.sh CMD...
|
|
150
|
+
stop = "docker rm -f $PP_QUEUE_CONTAINER" # how a stop reaches the work the runner started
|
|
151
|
+
log = "logs/{tag}.log" # under the project root; {tag} or {id}
|
|
145
152
|
env = { HF_HUB_OFFLINE = "1" }
|
|
146
153
|
```
|
|
147
154
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
A runner puts the work where no signal of pp's reaches it, so a project that
|
|
156
|
+
sets one says the stop as well, or the `add` is refused. pp hands the runner
|
|
157
|
+
`PP_QUEUE_CONTAINER`, a name for whatever it starts elsewhere, and
|
|
158
|
+
`PP_QUEUE_ENVFILE`, a file of `KEY=VALUE` lines for a container to read with
|
|
159
|
+
`--env-file`. `pp queue add --no-runner` skips the runner for one job, and
|
|
160
|
+
`--runner CMD` with `--stop CMD` gives one job both with no `.pp.toml`.
|
|
161
|
+
|
|
162
|
+
A campaign is a file of one job per line, each line with its own flags, and a
|
|
163
|
+
stage can wait for the one before it:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
IDS=$(pp queue add --from stage1.txt) # one id per line on stdout
|
|
167
|
+
pp queue add --from stage2.txt --after "$(echo "$IDS" | tr '\n' ',')"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Give a job a token or a secret with `--env` or the project's `env`: a variable
|
|
171
|
+
exported in your shell reaches it only through the worker. `--json` gives a
|
|
172
|
+
job's variables by name, with each value `***`. `pp help queue` is the whole
|
|
173
|
+
page, with the runner to copy, and `pp help queue add` every option of `add`.
|
|
154
174
|
|
|
155
175
|
## Working with agents
|
|
156
176
|
|
|
@@ -209,6 +229,10 @@ pp promote rollback notes back to the release before
|
|
|
209
229
|
database migration that cannot be undone does not belong in it. pp keeps the
|
|
210
230
|
last five releases, and always keeps the current one and the one before.
|
|
211
231
|
|
|
232
|
+
Each hook's output goes to a file beside the run's log, and pp shows it as it
|
|
233
|
+
comes. pp waits for the hook itself, not for what the hook leaves running, so
|
|
234
|
+
redirect the output of a process an `after` starts.
|
|
235
|
+
|
|
212
236
|
A systemd service runs the release by pointing at `current`:
|
|
213
237
|
|
|
214
238
|
```
|
|
@@ -240,7 +264,7 @@ pp http ~/notes . a tab for each folder, at http://127.0.0.1:8000
|
|
|
240
264
|
```
|
|
241
265
|
|
|
242
266
|
`pp http` listens on 127.0.0.1 unless you pass `--bind`, and does not serve
|
|
243
|
-
files such as `.env` or private keys.
|
|
267
|
+
`.git` or files such as `.env` or private keys.
|
|
244
268
|
|
|
245
269
|
## Your own commands
|
|
246
270
|
|
|
@@ -292,7 +316,7 @@ too. To add a command to pp itself, see [CONTRIBUTING.md](https://github.com/xfl
|
|
|
292
316
|
pp is new, and its version is 0.x. A command or a config key may still
|
|
293
317
|
change, and so may a field of `--json` output; the
|
|
294
318
|
[changelog](https://github.com/xflash96/pp/blob/main/CHANGELOG.md) says when one does. CI runs the tests on
|
|
295
|
-
Python 3.11 and 3.14, on Ubuntu 24.04.
|
|
319
|
+
Python 3.11.4 and 3.14, on Ubuntu 24.04.
|
|
296
320
|
|
|
297
321
|
## Contributing
|
|
298
322
|
|