localqueue 0.3.4__tar.gz → 0.4.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.
- {localqueue-0.3.4 → localqueue-0.4.1}/CHANGELOG.md +34 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/PKG-INFO +16 -3
- {localqueue-0.3.4 → localqueue-0.4.1}/README.md +15 -2
- localqueue-0.4.1/docs/develop.md +137 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/index.md +17 -5
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/queues.md +19 -4
- localqueue-0.4.1/docs/release.md +47 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/retries.md +21 -8
- localqueue-0.4.1/docs/use-cases/local-outbox.md +83 -0
- localqueue-0.4.1/docs/use-cases/operator-recovery.md +85 -0
- localqueue-0.4.1/docs/use-cases/persistent-retries.md +74 -0
- localqueue-0.4.1/docs/use-cases.md +54 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/cli.py +10 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/pyproject.toml +1 -1
- {localqueue-0.3.4 → localqueue-0.4.1}/tests/test_cli.py +7 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/uv.lock +1 -1
- localqueue-0.3.4/docs/release.md +0 -111
- {localqueue-0.3.4 → localqueue-0.4.1}/.gitignore +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/LICENSE +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/api.md +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/compare.md +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/operational-maturity.md +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/docs/stability.md +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/email_worker.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/enqueue_email.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/process_webhook.sh +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/retry_demo.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/sqlite_concurrency_benchmark.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/examples/sqlite_process_harness.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/__init__.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/cli_commands.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/cli_support.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/cli_worker_commands.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/failure.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/paths.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/queue.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/__init__.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/__init__.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/_shared.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/base.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/lmdb.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/memory.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/stores/sqlite.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/retry/tenacity.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/services/__init__.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/services/queue_worker.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/__init__.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/_shared.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/base.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/lmdb.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/memory.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/stores/sqlite.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/localqueue/worker.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/tests/test_process_harness.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/tests/test_queue.py +0 -0
- {localqueue-0.3.4 → localqueue-0.4.1}/tests/test_retry.py +0 -0
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.1](https://github.com/brunoportis/localqueue/compare/v0.4.0...v0.4.1) (2026-04-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Documentation
|
|
7
|
+
|
|
8
|
+
* reposition use cases and guides ([8e98379](https://github.com/brunoportis/localqueue/commit/8e98379c29091b414ad437facbdb4d7a7c45db4c))
|
|
9
|
+
* reposition use cases and guides ([280288b](https://github.com/brunoportis/localqueue/commit/280288b02f877d78d2df067df67ce14a800cbfe2))
|
|
10
|
+
* simplify queue examples ([ed488b2](https://github.com/brunoportis/localqueue/commit/ed488b2ab210369331fbcda3568760f0da24618b))
|
|
11
|
+
* split use cases into dedicated pages ([5496826](https://github.com/brunoportis/localqueue/commit/54968266b0733b009147c56761ce9fe29889d5e9))
|
|
12
|
+
|
|
13
|
+
## [0.4.0](https://github.com/brunoportis/localqueue/compare/v0.3.4...v0.4.0) (2026-04-24)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* add release-please automation and CLI --version option ([f8db976](https://github.com/brunoportis/localqueue/commit/f8db9767f45fc1eb5055ef0e7eb946aae3af3129))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **release:** target uv.lock version explicitly ([3e71f44](https://github.com/brunoportis/localqueue/commit/3e71f4450e50de69488b18c04b018a6af00bf962))
|
|
24
|
+
* **release:** target uv.lock version explicitly ([31b45ce](https://github.com/brunoportis/localqueue/commit/31b45ce2ba3deea070d8d49c8bebebadcbc9fb37))
|
|
25
|
+
* **release:** track uv.lock in release-please ([897b86a](https://github.com/brunoportis/localqueue/commit/897b86aa33d9b38cba82da7b0e2623473488b94d))
|
|
26
|
+
* **release:** track uv.lock in release-please ([637ef7f](https://github.com/brunoportis/localqueue/commit/637ef7fca945f1153b643f27b0d6df6ccc39f778))
|
|
27
|
+
* **workflow:** configure git identity for release sync ([9872e7a](https://github.com/brunoportis/localqueue/commit/9872e7a85d1382a968f344444009e721da926e50))
|
|
28
|
+
* **workflow:** configure git identity for release sync ([2ef15fd](https://github.com/brunoportis/localqueue/commit/2ef15fdd3b5a877569b46980ef795442c1f3089c))
|
|
29
|
+
* **workflow:** sync uv.lock in release-please ([f26b1fa](https://github.com/brunoportis/localqueue/commit/f26b1fac2603f5ae0016ce994f5065291b6a6ad0))
|
|
30
|
+
* **workflow:** sync uv.lock in release-please ([193b4c6](https://github.com/brunoportis/localqueue/commit/193b4c6d5dba208ec43d7ea6ada402c5c4f1c2a5))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
|
|
35
|
+
* **use-cases:** simplify CLI examples ([84b92f2](https://github.com/brunoportis/localqueue/commit/84b92f2a420ddeb46dc8db2e6c1493fc45430bff))
|
|
36
|
+
|
|
3
37
|
## 0.3.4
|
|
4
38
|
|
|
5
39
|
- Keep `queue exec` and `queue process` alive in `--forever --block` mode when the queue starts empty.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: localqueue
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Durable local queues for Python, with persistent retry state powered by Tenacity.
|
|
5
5
|
Project-URL: Homepage, https://brunoportis.github.io/localqueue/
|
|
6
6
|
Project-URL: Documentation, https://brunoportis.github.io/localqueue/
|
|
@@ -40,9 +40,17 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
[](https://pypi.org/project/localqueue/)
|
|
41
41
|

|
|
42
42
|
|
|
43
|
-
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
43
|
+
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
44
|
+
local filesystem by default and keeps retry state with Tenacity.
|
|
44
45
|
|
|
45
|
-
Use it
|
|
46
|
+
Use it when the work can stay local and you want one of these outcomes:
|
|
47
|
+
|
|
48
|
+
- accept work now and perform the side effect later on the same machine
|
|
49
|
+
- inspect failed jobs and replay them from the terminal
|
|
50
|
+
- keep retry budgets across process restarts without adding a broker
|
|
51
|
+
|
|
52
|
+
It fits scripts, CLI tools, cron jobs, and small Python workers that share one
|
|
53
|
+
local store.
|
|
46
54
|
|
|
47
55
|
```bash
|
|
48
56
|
localqueue queue exec emails -- python scripts/send_email.py
|
|
@@ -61,6 +69,11 @@ def send_email(job: dict[str, str]) -> None:
|
|
|
61
69
|
|
|
62
70
|
Full docs live at [brunoportis.github.io/localqueue](https://brunoportis.github.io/localqueue/). The source docs are in [`docs/`](docs/).
|
|
63
71
|
|
|
72
|
+
Start with [`docs/use-cases.md`](docs/use-cases.md) if you want the shortest
|
|
73
|
+
path to deciding whether the project fits your workflow.
|
|
74
|
+
|
|
75
|
+
For development and release workflow details, see [`docs/develop.md`](docs/develop.md).
|
|
76
|
+
|
|
64
77
|
## Install
|
|
65
78
|
|
|
66
79
|
```bash
|
|
@@ -5,9 +5,17 @@
|
|
|
5
5
|
[](https://pypi.org/project/localqueue/)
|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
8
|
+
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
9
|
+
local filesystem by default and keeps retry state with Tenacity.
|
|
9
10
|
|
|
10
|
-
Use it
|
|
11
|
+
Use it when the work can stay local and you want one of these outcomes:
|
|
12
|
+
|
|
13
|
+
- accept work now and perform the side effect later on the same machine
|
|
14
|
+
- inspect failed jobs and replay them from the terminal
|
|
15
|
+
- keep retry budgets across process restarts without adding a broker
|
|
16
|
+
|
|
17
|
+
It fits scripts, CLI tools, cron jobs, and small Python workers that share one
|
|
18
|
+
local store.
|
|
11
19
|
|
|
12
20
|
```bash
|
|
13
21
|
localqueue queue exec emails -- python scripts/send_email.py
|
|
@@ -26,6 +34,11 @@ def send_email(job: dict[str, str]) -> None:
|
|
|
26
34
|
|
|
27
35
|
Full docs live at [brunoportis.github.io/localqueue](https://brunoportis.github.io/localqueue/). The source docs are in [`docs/`](docs/).
|
|
28
36
|
|
|
37
|
+
Start with [`docs/use-cases.md`](docs/use-cases.md) if you want the shortest
|
|
38
|
+
path to deciding whether the project fits your workflow.
|
|
39
|
+
|
|
40
|
+
For development and release workflow details, see [`docs/develop.md`](docs/develop.md).
|
|
41
|
+
|
|
29
42
|
## Install
|
|
30
43
|
|
|
31
44
|
```bash
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/hammer
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Development Guide
|
|
6
|
+
|
|
7
|
+
This page describes the day-to-day development flow for `localqueue`: how to
|
|
8
|
+
write commits, how versions are released, what quality gates should pass before
|
|
9
|
+
merging, and what happens to docs and deployments.
|
|
10
|
+
|
|
11
|
+
## Commit style
|
|
12
|
+
|
|
13
|
+
Use Conventional Commits.
|
|
14
|
+
|
|
15
|
+
Preferred shapes:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
feat: add queue health summary
|
|
19
|
+
fix: keep forever workers alive on empty queues
|
|
20
|
+
docs: describe release flow
|
|
21
|
+
chore: refresh release-please config
|
|
22
|
+
test: cover version output in the CLI
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Keep commit messages short and specific. Use a scope only when it helps the
|
|
26
|
+
history, for example `fix(cli): ...` or `docs(release): ...`.
|
|
27
|
+
|
|
28
|
+
Why this matters here:
|
|
29
|
+
|
|
30
|
+
- `release-please` uses commit history to decide the next version.
|
|
31
|
+
- clean commit messages produce cleaner changelogs.
|
|
32
|
+
- release PRs become easier to review when each change has a clear intent.
|
|
33
|
+
|
|
34
|
+
## Versioning
|
|
35
|
+
|
|
36
|
+
Versions follow SemVer-style `MAJOR.MINOR.PATCH`.
|
|
37
|
+
|
|
38
|
+
Before `1.0.0`, use this rule:
|
|
39
|
+
|
|
40
|
+
| Change | Version bump |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Bug fix only | patch |
|
|
43
|
+
| New CLI/API feature | minor |
|
|
44
|
+
| Breaking user-facing behavior | minor, with a clear changelog note |
|
|
45
|
+
|
|
46
|
+
In this repo:
|
|
47
|
+
|
|
48
|
+
- `release-please` opens or updates the release PR.
|
|
49
|
+
- merging that release PR creates the tag and the GitHub Release.
|
|
50
|
+
- the release tag stays in the form `v<version>`.
|
|
51
|
+
- `pyproject.toml` is updated by the release automation, not by hand.
|
|
52
|
+
|
|
53
|
+
## Quality gates before merge
|
|
54
|
+
|
|
55
|
+
Run the project checks before opening or merging a release-worthy change.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run ruff format .
|
|
59
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run ruff format --check .
|
|
60
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run pytest -q
|
|
61
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run ruff check .
|
|
62
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run basedpyright
|
|
63
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv run zensical build --clean
|
|
64
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv lock --check
|
|
65
|
+
env UV_CACHE_DIR=/tmp/uv-cache uv build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Practical notes:
|
|
69
|
+
|
|
70
|
+
- `pytest` is the main behavior gate.
|
|
71
|
+
- `ruff format` applies the formatting fix locally.
|
|
72
|
+
- `ruff format --check` verifies formatting stays stable before a merge.
|
|
73
|
+
- `ruff check` and `basedpyright` keep the codebase clean and typed where it
|
|
74
|
+
matters.
|
|
75
|
+
- `zensical build --clean` catches docs breakage before it lands.
|
|
76
|
+
- `uv lock --check` confirms the lockfile is current.
|
|
77
|
+
- `uv build` verifies the package artifacts still build cleanly.
|
|
78
|
+
|
|
79
|
+
## Documentation workflow
|
|
80
|
+
|
|
81
|
+
Docs should stay close to the code and the release flow.
|
|
82
|
+
|
|
83
|
+
- Update `docs/` when the CLI, public API, or release process changes.
|
|
84
|
+
- Keep `docs/release.md` focused on release automation and tagging.
|
|
85
|
+
- Keep `docs/operational-maturity.md` focused on runtime guarantees and
|
|
86
|
+
production boundaries.
|
|
87
|
+
- Update `docs/index.md` and the docs navigation when a new top-level doc is
|
|
88
|
+
added.
|
|
89
|
+
|
|
90
|
+
Good documentation changes usually include:
|
|
91
|
+
|
|
92
|
+
- a short user-facing description
|
|
93
|
+
- exact commands
|
|
94
|
+
- concrete examples
|
|
95
|
+
- the current expected release or deployment behavior
|
|
96
|
+
|
|
97
|
+
## Deployments
|
|
98
|
+
|
|
99
|
+
The docs site is built from `docs/` with `zensical`.
|
|
100
|
+
|
|
101
|
+
The release workflow currently handles:
|
|
102
|
+
|
|
103
|
+
- PyPI publication
|
|
104
|
+
- GHCR image publication
|
|
105
|
+
|
|
106
|
+
The docs workflow currently handles:
|
|
107
|
+
|
|
108
|
+
- building the docs site on pushes and pull requests
|
|
109
|
+
- deploying the site from GitHub Actions on push to the configured branches
|
|
110
|
+
|
|
111
|
+
If a change affects packaging, release tags, or public docs, validate both the
|
|
112
|
+
package build and the docs build before merging.
|
|
113
|
+
|
|
114
|
+
## Local release checklist
|
|
115
|
+
|
|
116
|
+
When preparing a release or release PR, verify:
|
|
117
|
+
|
|
118
|
+
1. commits follow Conventional Commits
|
|
119
|
+
2. tests pass
|
|
120
|
+
3. lint and typecheck pass
|
|
121
|
+
4. docs build
|
|
122
|
+
5. package build succeeds
|
|
123
|
+
6. the release notes and changelog reflect the change
|
|
124
|
+
|
|
125
|
+
## Secrets and CI
|
|
126
|
+
|
|
127
|
+
The current release automation expects:
|
|
128
|
+
|
|
129
|
+
- `RELEASE_PLEASE_TOKEN` for `release-please`
|
|
130
|
+
- `SONAR_TOKEN` for SonarCloud, if the Sonar workflow is enabled
|
|
131
|
+
|
|
132
|
+
Keep secrets in repository settings, not in the repo.
|
|
133
|
+
|
|
134
|
+
## Daily rule
|
|
135
|
+
|
|
136
|
+
If a change would confuse the next release PR, fix the commit message, docs, or
|
|
137
|
+
tests before merging it.
|
|
@@ -4,19 +4,30 @@ icon: lucide/inbox
|
|
|
4
4
|
|
|
5
5
|
# Overview
|
|
6
6
|
|
|
7
|
-
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
7
|
+
`localqueue` is a small durable queue for one machine. It stores work on the
|
|
8
|
+
local filesystem by default and keeps retry state with Tenacity.
|
|
8
9
|
|
|
9
|
-
It fits scripts, CLI tools, cron jobs, development helpers, and small workers
|
|
10
|
+
It fits scripts, CLI tools, cron jobs, development helpers, and small workers
|
|
11
|
+
that can safely share one local store. It is not a distributed broker and does
|
|
12
|
+
not provide multi-host coordination.
|
|
10
13
|
|
|
11
14
|
!!! note
|
|
12
15
|
The default model is local-file storage, at-least-once delivery, and best-effort ordering under concurrency.
|
|
13
16
|
|
|
17
|
+
## Best-fit workflows
|
|
18
|
+
|
|
19
|
+
`localqueue` is strongest in three situations:
|
|
20
|
+
|
|
21
|
+
- local outbox workflows where the caller should return before the side effect happens
|
|
22
|
+
- terminal-driven recovery where operators need dead-letter inspection and replay
|
|
23
|
+
- persistent retry budgets for work that already arrives from somewhere else
|
|
24
|
+
|
|
14
25
|
## CLI
|
|
15
26
|
|
|
16
27
|
Use the CLI when you want to enqueue, inspect, watch, and recover jobs from the terminal.
|
|
17
28
|
|
|
18
29
|
```bash
|
|
19
|
-
|
|
30
|
+
echo '{"to":"user@example.com"}' | localqueue queue add emails
|
|
20
31
|
localqueue queue exec emails -- python scripts/send_email.py
|
|
21
32
|
localqueue queue stats emails --watch --interval 1
|
|
22
33
|
localqueue queue dead emails --summary
|
|
@@ -42,14 +53,13 @@ def send_email(job: dict[str, str]) -> None:
|
|
|
42
53
|
deliver(job["to"])
|
|
43
54
|
```
|
|
44
55
|
|
|
45
|
-
`localqueue.retry` stays available when another system already delivers work and you only need retry state.
|
|
46
|
-
|
|
47
56
|
See [Persistent retries](retries.md) for the retry API and store options.
|
|
48
57
|
|
|
49
58
|
## When to use
|
|
50
59
|
|
|
51
60
|
- small Python workers on one machine
|
|
52
61
|
- scripts that need durable work after restarts
|
|
62
|
+
- local outbox workflows for emails, webhooks, uploads, or reports
|
|
53
63
|
- terminal-driven queues for jobs you want to inspect and requeue
|
|
54
64
|
- retry state that must survive process restarts
|
|
55
65
|
|
|
@@ -65,10 +75,12 @@ See [Compare](compare.md) for a short decision guide.
|
|
|
65
75
|
|
|
66
76
|
## Read more
|
|
67
77
|
|
|
78
|
+
- [Use cases](use-cases.md)
|
|
68
79
|
- [Persistent queues](queues.md)
|
|
69
80
|
- [Persistent retries](retries.md)
|
|
70
81
|
- [Stability](stability.md)
|
|
71
82
|
- [Compare](compare.md)
|
|
72
83
|
- [API reference](api.md)
|
|
73
84
|
- [Operational maturity](operational-maturity.md)
|
|
85
|
+
- [Development guide](develop.md)
|
|
74
86
|
- [Release checklist](release.md)
|
|
@@ -4,14 +4,28 @@ icon: lucide/inbox
|
|
|
4
4
|
|
|
5
5
|
# Persistent Queues
|
|
6
6
|
|
|
7
|
-
`localqueue`
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
`localqueue` queues are for local workflows where you want to accept work now,
|
|
8
|
+
run it later on the same machine, and keep enough state to inspect and recover
|
|
9
|
+
failures from the terminal.
|
|
10
|
+
|
|
11
|
+
This is the main workflow API in the project. It is a small queue abstraction
|
|
12
|
+
backed by SQLite by default and designed for durable, at-least-once delivery of
|
|
13
|
+
Python values.
|
|
10
14
|
|
|
11
15
|
It is local infrastructure: producers and consumers should run where they can
|
|
12
16
|
share the same queue store safely. It is not a distributed broker and does not
|
|
13
17
|
provide multi-host coordination.
|
|
14
18
|
|
|
19
|
+
Best-fit queue workflows:
|
|
20
|
+
|
|
21
|
+
- local outbox patterns for email, webhooks, uploads, or report generation
|
|
22
|
+
- CLI-driven workers that should survive restarts
|
|
23
|
+
- jobs that may need dead-letter inspection and replay
|
|
24
|
+
|
|
25
|
+
Start somewhere else if the queue itself needs to be a distributed system
|
|
26
|
+
boundary. `localqueue` keeps delivery local, recovery local, and operations
|
|
27
|
+
simple.
|
|
28
|
+
|
|
15
29
|
If you have independent workloads, give them separate queue names instead of
|
|
16
30
|
loading everything into one queue. Each queue keeps its own stats, dead
|
|
17
31
|
letters, retention settings, and worker identity history.
|
|
@@ -30,7 +44,8 @@ queue = PersistentQueue(
|
|
|
30
44
|
)
|
|
31
45
|
```
|
|
32
46
|
|
|
33
|
-
The queue name identifies an independent stream inside the store. Queue names
|
|
47
|
+
The queue name identifies an independent stream inside the store. Queue names
|
|
48
|
+
cannot be empty and cannot contain `:`.
|
|
34
49
|
Queue-level retry defaults are merged into worker retry kwargs before explicit
|
|
35
50
|
worker overrides, so the same policy can be reused across several workers that
|
|
36
51
|
consume the same queue.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/package-check
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Release
|
|
6
|
+
|
|
7
|
+
This project uses `release-please` to automate release PRs, changelog updates,
|
|
8
|
+
version bumps, Git tags, and GitHub Releases. The PyPI and GHCR publication
|
|
9
|
+
still happens in the existing release workflow after the release tag is
|
|
10
|
+
created.
|
|
11
|
+
|
|
12
|
+
## Versioning
|
|
13
|
+
|
|
14
|
+
Use SemVer-style versions:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
MAJOR.MINOR.PATCH
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Before `1.0.0`, use this convention:
|
|
21
|
+
|
|
22
|
+
| Change | Version bump |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Bug fix only | patch, for example `0.1.1` |
|
|
25
|
+
| New CLI/API feature | minor, for example `0.2.0` |
|
|
26
|
+
| Breaking user-facing behavior | minor before `1.0.0`, with a clear changelog note |
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
1. Merge Conventional Commit changes into `master`.
|
|
31
|
+
2. `release-please` opens or updates a release PR with the changelog and the
|
|
32
|
+
version bump.
|
|
33
|
+
3. Merge the release PR.
|
|
34
|
+
4. `release-please` tags the merge commit and creates the GitHub Release.
|
|
35
|
+
5. The existing `Release` workflow publishes the artifacts to PyPI and GHCR.
|
|
36
|
+
|
|
37
|
+
## Setup notes
|
|
38
|
+
|
|
39
|
+
1. The workflow expects a repository secret named `RELEASE_PLEASE_TOKEN`.
|
|
40
|
+
2. The release tag format stays `v<version>` so the current publish workflow can
|
|
41
|
+
keep matching `v*` tags.
|
|
42
|
+
3. Keep commit messages in Conventional Commit form so `release-please` can
|
|
43
|
+
classify changes correctly.
|
|
44
|
+
|
|
45
|
+
## Current Release
|
|
46
|
+
|
|
47
|
+
The current planned release is `0.3.4`.
|
|
@@ -4,14 +4,27 @@ icon: lucide/rotate-cw
|
|
|
4
4
|
|
|
5
5
|
# Persistent Retries
|
|
6
6
|
|
|
7
|
-
`localqueue` is
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
`localqueue.retry` is for code that already receives the work but still needs a
|
|
8
|
+
retry budget that survives process restarts.
|
|
9
|
+
|
|
10
|
+
This is the durable retry layer used by queue workers and by code that already
|
|
11
|
+
has its own delivery mechanism. It wraps Tenacity's `Retrying` and
|
|
12
|
+
`AsyncRetrying` classes. You still configure `stop`, `wait`, `retry`,
|
|
13
|
+
callbacks, and `retry_with()` the same way; the wrapper adds a durable attempt
|
|
14
|
+
record before each stop decision.
|
|
15
|
+
|
|
16
|
+
Best-fit retry workflows:
|
|
17
|
+
|
|
18
|
+
- cron jobs that call fragile external services
|
|
19
|
+
- HTTP handlers or consumers that need durable retry budgets per logical job
|
|
20
|
+
- scripts where retry state must outlive the current process
|
|
21
|
+
|
|
22
|
+
Use this layer when the missing piece is persistent attempt tracking. If you
|
|
23
|
+
need ack, release, leases, dead-letter records, or queue inspection, start with
|
|
24
|
+
[Persistent queues](queues.md) instead.
|
|
25
|
+
|
|
26
|
+
The main design constraint is stable identity: each logical job needs a stable
|
|
27
|
+
retry key so the same work does not retry forever under a new process.
|
|
15
28
|
|
|
16
29
|
## Decorator API
|
|
17
30
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/mail
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Local Outbox
|
|
6
|
+
|
|
7
|
+
Use this pattern when the caller should return now and the external side effect
|
|
8
|
+
can happen a few seconds later on the same machine.
|
|
9
|
+
|
|
10
|
+
This is one of the strongest `localqueue` stories: accept work quickly, store
|
|
11
|
+
it durably, and let a local worker perform the slow or fragile side effect
|
|
12
|
+
later.
|
|
13
|
+
|
|
14
|
+
## Best fit
|
|
15
|
+
|
|
16
|
+
- email delivery from a web app or admin script
|
|
17
|
+
- webhook fan-out from a local integration process
|
|
18
|
+
- uploads, report generation, or notifications that should not block the caller
|
|
19
|
+
|
|
20
|
+
## Why `localqueue` fits
|
|
21
|
+
|
|
22
|
+
- no broker, Redis, or extra service to operate
|
|
23
|
+
- queue state survives process restarts
|
|
24
|
+
- the worker can be a Python function or an external command
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
- this is a single-host pattern, not a distributed outbox
|
|
29
|
+
- delivery is at least once, so handlers should be idempotent
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
Create a scratch directory and a small worker script once:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
mkdir -p /tmp/localqueue-use-cases
|
|
37
|
+
cat > /tmp/localqueue-use-cases/email_worker.py <<'PY'
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import sys
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
payload = json.load(sys.stdin)
|
|
45
|
+
address = payload["to"]
|
|
46
|
+
if payload.get("fail"):
|
|
47
|
+
raise ConnectionError(f"could not deliver email to {address}")
|
|
48
|
+
print(f"sent email to {address}")
|
|
49
|
+
PY
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Minimal flow
|
|
53
|
+
|
|
54
|
+
Enqueue one email job:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
echo '{"to":"user@example.com"}' | localqueue queue add emails
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Inspect the queue before processing:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
localqueue queue stats emails
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Process one queued message with the worker script:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
localqueue queue exec emails \
|
|
70
|
+
-- python /tmp/localqueue-use-cases/email_worker.py
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Check that the queue is empty again:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
localqueue queue stats emails
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## When to choose something else
|
|
80
|
+
|
|
81
|
+
Use a broker or managed queue instead when producers and consumers run on
|
|
82
|
+
different machines, strict global ordering matters, or the queue is part of a
|
|
83
|
+
distributed system boundary.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/life-buoy
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Operator Recovery
|
|
6
|
+
|
|
7
|
+
Use this pattern when jobs can fail in recoverable ways and operators need to
|
|
8
|
+
inspect, dead-letter, and replay them from the terminal.
|
|
9
|
+
|
|
10
|
+
This is where `localqueue` feels more useful than a pile of shell scripts:
|
|
11
|
+
failures stay visible, recovery is explicit, and replay does not require a
|
|
12
|
+
custom one-off script.
|
|
13
|
+
|
|
14
|
+
## Best fit
|
|
15
|
+
|
|
16
|
+
- webhook delivery with occasional upstream outages
|
|
17
|
+
- notification pipelines that need human inspection on failure
|
|
18
|
+
- CLI-driven maintenance jobs where operators need a dead-letter queue
|
|
19
|
+
|
|
20
|
+
## Why `localqueue` fits
|
|
21
|
+
|
|
22
|
+
- failed jobs can move to dead-letter after a small retry budget
|
|
23
|
+
- operators can inspect summary and full records locally
|
|
24
|
+
- replay is one command instead of ad hoc recovery code
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
- recovery is local to one machine and one shared store
|
|
29
|
+
- this does not replace broker-level operations across multiple services
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
Create a scratch directory and a small worker script once:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
mkdir -p /tmp/localqueue-use-cases
|
|
37
|
+
cat > /tmp/localqueue-use-cases/email_worker.py <<'PY'
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import sys
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
payload = json.load(sys.stdin)
|
|
45
|
+
address = payload["to"]
|
|
46
|
+
if payload.get("fail"):
|
|
47
|
+
raise ConnectionError(f"could not deliver email to {address}")
|
|
48
|
+
print(f"sent email to {address}")
|
|
49
|
+
PY
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Minimal flow
|
|
53
|
+
|
|
54
|
+
Create a failing job:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
echo '{"to":"broken@example.com","fail":true}' | localqueue queue add webhooks
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Process it with a small retry budget so it reaches dead-letter quickly:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
localqueue queue exec webhooks \
|
|
64
|
+
--max-tries 2 \
|
|
65
|
+
-- python /tmp/localqueue-use-cases/email_worker.py
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Inspect the dead-letter summary and full record:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
localqueue queue dead webhooks --summary
|
|
72
|
+
|
|
73
|
+
localqueue queue dead webhooks
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Replay everything in the dead-letter queue:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
localqueue queue requeue-dead webhooks --all
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## When to choose something else
|
|
83
|
+
|
|
84
|
+
Use a different system when recovery, retention, and observability must work as
|
|
85
|
+
shared platform capabilities across multiple hosts or services.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/rotate-cw
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Persistent Retries
|
|
6
|
+
|
|
7
|
+
Use this pattern when another part of your system already delivers the work and
|
|
8
|
+
the missing piece is a retry budget that survives process restarts.
|
|
9
|
+
|
|
10
|
+
This page is about persistent attempt tracking, not a full queue lifecycle. If
|
|
11
|
+
you need ack, release, leases, or dead-letter handling, start with
|
|
12
|
+
`PersistentQueue` instead.
|
|
13
|
+
|
|
14
|
+
## Best fit
|
|
15
|
+
|
|
16
|
+
- cron jobs that call an external API
|
|
17
|
+
- HTTP handlers that already receive the work but need durable retry budgets
|
|
18
|
+
- consumers from another queueing system that want persistent retry state
|
|
19
|
+
|
|
20
|
+
## Why `localqueue` fits
|
|
21
|
+
|
|
22
|
+
- retry budgets survive process restarts
|
|
23
|
+
- a stable key prevents the same logical job from being retried forever
|
|
24
|
+
- exhausted jobs can be pruned later from the CLI
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
- this is retry state, not a full message lifecycle
|
|
29
|
+
- each logical job needs a stable retry key
|
|
30
|
+
|
|
31
|
+
## Minimal flow
|
|
32
|
+
|
|
33
|
+
Run this once in an environment where `localqueue` is importable.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python - <<'PY'
|
|
37
|
+
from localqueue.retry import PersistentRetryExhausted, persistent_retry
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@persistent_retry(
|
|
41
|
+
key="invoice:1001",
|
|
42
|
+
max_tries=3,
|
|
43
|
+
)
|
|
44
|
+
def flaky() -> None:
|
|
45
|
+
print("calling remote API")
|
|
46
|
+
raise ConnectionError("temporary upstream failure")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
flaky()
|
|
51
|
+
except PersistentRetryExhausted as exc:
|
|
52
|
+
print(f"exhausted: {exc.key} after {exc.attempts} attempts")
|
|
53
|
+
except Exception as exc:
|
|
54
|
+
print(f"failed this run: {exc}")
|
|
55
|
+
PY
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run the same command again. The retry state is reused because the key is
|
|
59
|
+
stable and the attempt store is on disk. After the budget is exhausted,
|
|
60
|
+
`PersistentRetryExhausted` is raised before the wrapped function runs again.
|
|
61
|
+
|
|
62
|
+
Inspect or prune exhausted retry records later:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
localqueue retry prune \
|
|
66
|
+
--dry-run \
|
|
67
|
+
--older-than 0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## When to choose something else
|
|
71
|
+
|
|
72
|
+
Use a queue instead when work must be stored for later delivery, acknowledged
|
|
73
|
+
after success, released for another attempt, or moved to dead-letter on final
|
|
74
|
+
failure.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/briefcase
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Use Cases
|
|
6
|
+
|
|
7
|
+
`localqueue` shines when the work can stay on one machine and the value is in
|
|
8
|
+
durable retries, local recovery, and simple terminal operations.
|
|
9
|
+
|
|
10
|
+
The pages in this section focus on workflows that are already well supported by
|
|
11
|
+
the current queue, worker, dead-letter, and persistent-retry APIs.
|
|
12
|
+
|
|
13
|
+
By default, `localqueue` stores queue and retry state under the usual XDG data
|
|
14
|
+
location for your user.
|
|
15
|
+
|
|
16
|
+
## The three strongest workflows
|
|
17
|
+
|
|
18
|
+
### Local outbox
|
|
19
|
+
|
|
20
|
+
Accept work now, run the external side effect later on the same machine.
|
|
21
|
+
|
|
22
|
+
This is the best fit when the caller should return quickly and the worker can
|
|
23
|
+
consume the job from a shared local store.
|
|
24
|
+
|
|
25
|
+
[Read the local outbox guide](use-cases/local-outbox.md)
|
|
26
|
+
|
|
27
|
+
### Operator recovery
|
|
28
|
+
|
|
29
|
+
Inspect failed jobs, review dead-letter records, and replay them from the
|
|
30
|
+
terminal after the underlying issue is fixed.
|
|
31
|
+
|
|
32
|
+
This is the best fit when a human operator needs a local recovery loop instead
|
|
33
|
+
of ad hoc shell scripts.
|
|
34
|
+
|
|
35
|
+
[Read the operator recovery guide](use-cases/operator-recovery.md)
|
|
36
|
+
|
|
37
|
+
### Persistent retries
|
|
38
|
+
|
|
39
|
+
Keep retry budgets across restarts when another part of the system already
|
|
40
|
+
delivers the work.
|
|
41
|
+
|
|
42
|
+
This is the best fit when you need durable attempt tracking but not a queue
|
|
43
|
+
lifecycle.
|
|
44
|
+
|
|
45
|
+
[Read the persistent retries guide](use-cases/persistent-retries.md)
|
|
46
|
+
|
|
47
|
+
## When these use cases do not fit
|
|
48
|
+
|
|
49
|
+
Use a broker or managed queue instead when:
|
|
50
|
+
|
|
51
|
+
- producers and consumers run on different machines
|
|
52
|
+
- strict global ordering matters
|
|
53
|
+
- very high write concurrency is expected
|
|
54
|
+
- queue retention, fan-out, or cross-service coordination is a first-class need
|
|
@@ -8,6 +8,7 @@ from contextlib import contextmanager
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Iterator, TYPE_CHECKING
|
|
10
10
|
|
|
11
|
+
from . import __version__
|
|
11
12
|
from .cli_commands import (
|
|
12
13
|
CommandRegistryContext,
|
|
13
14
|
register_config_commands,
|
|
@@ -75,6 +76,15 @@ def _build_app(typer: Any, yaml: Any, console: Any, err_console: Any) -> Any:
|
|
|
75
76
|
queue_app = typer.Typer(no_args_is_help=True)
|
|
76
77
|
retry_app = typer.Typer(no_args_is_help=True)
|
|
77
78
|
config_app = typer.Typer(no_args_is_help=True)
|
|
79
|
+
|
|
80
|
+
@app.callback(invoke_without_command=True)
|
|
81
|
+
def root_callback(
|
|
82
|
+
version: bool = typer.Option(False, "--version", help="Show version and exit"),
|
|
83
|
+
) -> None:
|
|
84
|
+
if version:
|
|
85
|
+
console.print(__version__)
|
|
86
|
+
raise typer.Exit()
|
|
87
|
+
|
|
78
88
|
command_context = CommandRegistryContext(
|
|
79
89
|
typer=typer,
|
|
80
90
|
console=console,
|
|
@@ -215,6 +215,13 @@ class CliTests(unittest.TestCase):
|
|
|
215
215
|
build_app.assert_called_once()
|
|
216
216
|
app.assert_called_once_with(args=["--help"])
|
|
217
217
|
|
|
218
|
+
def test_root_version_option_prints_version(self) -> None:
|
|
219
|
+
with mock.patch("localqueue.cli.__version__", "9.9.9"):
|
|
220
|
+
result = self._invoke(["--version"])
|
|
221
|
+
|
|
222
|
+
self.assertEqual(result.exit_code, 0)
|
|
223
|
+
self.assertEqual(result.stdout.strip(), "9.9.9")
|
|
224
|
+
|
|
218
225
|
def test_read_value_uses_argument_before_stdin(self) -> None:
|
|
219
226
|
self.assertEqual(_read_value("{}"), "{}")
|
|
220
227
|
|
localqueue-0.3.4/docs/release.md
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
icon: lucide/package-check
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Release
|
|
6
|
-
|
|
7
|
-
This project currently uses a manual versioning flow. The release version lives
|
|
8
|
-
in `pyproject.toml`, and each release should have a matching `CHANGELOG.md`
|
|
9
|
-
entry and Git tag.
|
|
10
|
-
|
|
11
|
-
## Versioning
|
|
12
|
-
|
|
13
|
-
Use SemVer-style versions:
|
|
14
|
-
|
|
15
|
-
```text
|
|
16
|
-
MAJOR.MINOR.PATCH
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Before `1.0.0`, use this convention:
|
|
20
|
-
|
|
21
|
-
| Change | Version bump |
|
|
22
|
-
| --- | --- |
|
|
23
|
-
| Bug fix only | patch, for example `0.1.1` |
|
|
24
|
-
| New CLI/API feature | minor, for example `0.2.0` |
|
|
25
|
-
| Breaking user-facing behavior | minor before `1.0.0`, with a clear changelog note |
|
|
26
|
-
|
|
27
|
-
## Checklist
|
|
28
|
-
|
|
29
|
-
1. Update `version` in `pyproject.toml`.
|
|
30
|
-
2. Run `uv lock` and commit the updated `uv.lock`.
|
|
31
|
-
3. Update `CHANGELOG.md`.
|
|
32
|
-
4. Confirm `requires-python`, license metadata, and PyPI classifiers still match
|
|
33
|
-
supported Python versions and the intended release policy.
|
|
34
|
-
5. Run the test suite:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
uv run pytest
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
6. Run lint and typecheck:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
uv run ruff check .
|
|
44
|
-
uv run basedpyright
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
7. Build the docs:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
uv run zensical build --clean
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
8. Confirm the lockfile is current:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
uv lock --check
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
9. Build the release artifacts:
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
uv build
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
10. Inspect the artifacts:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
tar -tzf dist/localqueue-<version>.tar.gz | less
|
|
69
|
-
python -m zipfile -l dist/localqueue-<version>-py3-none-any.whl
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
11. Install the wheel in a clean virtual environment:
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
uv venv /tmp/localqueue-release-venv
|
|
76
|
-
uv pip install --python /tmp/localqueue-release-venv/bin/python \
|
|
77
|
-
"dist/localqueue-<version>-py3-none-any.whl[all]"
|
|
78
|
-
/tmp/localqueue-release-venv/bin/localqueue --help
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
12. Run a local smoke test:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
uv run python examples/enqueue_email.py user@example.com \
|
|
85
|
-
--store-path /tmp/localqueue-smoke
|
|
86
|
-
|
|
87
|
-
uv run localqueue queue process emails examples.email_worker:send_email \
|
|
88
|
-
--store-path /tmp/localqueue-smoke \
|
|
89
|
-
--retry-store-path /tmp/localqueue-smoke-retries.sqlite3 \
|
|
90
|
-
--worker-id worker-smoke \
|
|
91
|
-
--max-tries 3
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
13. Create the release tag:
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
git tag v<version>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
14. Publish the artifacts.
|
|
101
|
-
|
|
102
|
-
This release workflow also publishes the CLI image to GitHub Container
|
|
103
|
-
Registry as `ghcr.io/brunoportis/localqueue:<tag>` and `:latest`.
|
|
104
|
-
|
|
105
|
-
15. Confirm the docs deployment finished successfully in the `Documentation`
|
|
106
|
-
workflow. GitHub Pages must be configured to deploy from GitHub Actions in
|
|
107
|
-
the repository settings.
|
|
108
|
-
|
|
109
|
-
## Current Release
|
|
110
|
-
|
|
111
|
-
The current planned release is `0.3.4`.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|