h2hdb-ingest 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.
- h2hdb_ingest-0.1.0/.github/workflows/publish.yml +123 -0
- h2hdb_ingest-0.1.0/.gitignore +13 -0
- h2hdb_ingest-0.1.0/AGENTS.md +87 -0
- h2hdb_ingest-0.1.0/CLAUDE.md +85 -0
- h2hdb_ingest-0.1.0/LICENSE +674 -0
- h2hdb_ingest-0.1.0/PKG-INFO +906 -0
- h2hdb_ingest-0.1.0/README.md +204 -0
- h2hdb_ingest-0.1.0/mypy.ini +18 -0
- h2hdb_ingest-0.1.0/pyproject.toml +58 -0
- h2hdb_ingest-0.1.0/scripts/bootstrap-catalog.py +7 -0
- h2hdb_ingest-0.1.0/scripts/hooks/finalize-markdown.sh +28 -0
- h2hdb_ingest-0.1.0/scripts/hooks/finalize-python.sh +26 -0
- h2hdb_ingest-0.1.0/scripts/rebuild-env.sh +17 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/__init__.py +32 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/__main__.py +66 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/bootstrap.py +58 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/cbz.py +1248 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/config.py +137 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/deduplication.py +164 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/models.py +94 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/naming.py +11 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/py.typed +1 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/resident.py +240 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/scanner.py +258 -0
- h2hdb_ingest-0.1.0/src/h2hdb_ingest/service.py +259 -0
- h2hdb_ingest-0.1.0/tests/test_bootstrap_catalog.py +79 -0
- h2hdb_ingest-0.1.0/tests/test_cbz_current_view.py +900 -0
- h2hdb_ingest-0.1.0/tests/test_cli.py +97 -0
- h2hdb_ingest-0.1.0/tests/test_config.py +142 -0
- h2hdb_ingest-0.1.0/tests/test_ingest.py +1193 -0
- h2hdb_ingest-0.1.0/tests/test_package.py +21 -0
- h2hdb_ingest-0.1.0/tests/test_resident.py +304 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- pyproject.toml
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
validate:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
bumped: ${{ steps.version.outputs.bumped }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 2
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.14"
|
|
25
|
+
|
|
26
|
+
- name: Install packaging tools
|
|
27
|
+
run: python -m pip install --upgrade packaging build
|
|
28
|
+
|
|
29
|
+
- name: Check version bump
|
|
30
|
+
id: version
|
|
31
|
+
run: |
|
|
32
|
+
python - <<'PY'
|
|
33
|
+
import os
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
import subprocess
|
|
36
|
+
import tomllib
|
|
37
|
+
|
|
38
|
+
from packaging.version import Version
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def project_version(document: str) -> Version:
|
|
42
|
+
return Version(str(tomllib.loads(document)["project"]["version"]))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
current = project_version(
|
|
46
|
+
Path("pyproject.toml").read_text(encoding="utf-8")
|
|
47
|
+
)
|
|
48
|
+
previous_document = subprocess.run(
|
|
49
|
+
["git", "show", "HEAD~1:pyproject.toml"],
|
|
50
|
+
check=False,
|
|
51
|
+
capture_output=True,
|
|
52
|
+
text=True,
|
|
53
|
+
)
|
|
54
|
+
previous = (
|
|
55
|
+
project_version(previous_document.stdout)
|
|
56
|
+
if previous_document.returncode == 0
|
|
57
|
+
else None
|
|
58
|
+
)
|
|
59
|
+
if previous is not None and current < previous:
|
|
60
|
+
raise SystemExit(
|
|
61
|
+
f"project.version decreased from {previous} to {current}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
bumped = previous is None or current > previous
|
|
65
|
+
output_path = Path(os.environ["GITHUB_OUTPUT"])
|
|
66
|
+
with output_path.open("a", encoding="utf-8") as output:
|
|
67
|
+
print(f"current={current}", file=output)
|
|
68
|
+
print(f"previous={previous or ''}", file=output)
|
|
69
|
+
print(f"bumped={'true' if bumped else 'false'}", file=output)
|
|
70
|
+
PY
|
|
71
|
+
|
|
72
|
+
- name: Install project and development dependencies
|
|
73
|
+
run: python -m pip install -e ".[dev]"
|
|
74
|
+
|
|
75
|
+
- name: Check formatting
|
|
76
|
+
run: python -m black --check .
|
|
77
|
+
|
|
78
|
+
- name: Lint
|
|
79
|
+
run: python -m ruff check .
|
|
80
|
+
|
|
81
|
+
- name: Type-check
|
|
82
|
+
run: >-
|
|
83
|
+
python -m mypy src tests
|
|
84
|
+
scripts/bootstrap-catalog.py
|
|
85
|
+
|
|
86
|
+
- name: Test
|
|
87
|
+
run: python -m pytest
|
|
88
|
+
|
|
89
|
+
- name: Build distributions
|
|
90
|
+
run: python -m build
|
|
91
|
+
|
|
92
|
+
- name: Smoke-test wheel
|
|
93
|
+
run: |
|
|
94
|
+
WHEEL="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
|
|
95
|
+
test -n "$WHEEL"
|
|
96
|
+
python -m pip install --force-reinstall --no-deps "$WHEEL"
|
|
97
|
+
python -c "import h2hdb_ingest"
|
|
98
|
+
h2hdb-ingest --help
|
|
99
|
+
h2hdb-ingest-bootstrap --help
|
|
100
|
+
|
|
101
|
+
- name: Store distributions
|
|
102
|
+
if: steps.version.outputs.bumped == 'true'
|
|
103
|
+
uses: actions/upload-artifact@v4
|
|
104
|
+
with:
|
|
105
|
+
name: distributions
|
|
106
|
+
path: dist/
|
|
107
|
+
if-no-files-found: error
|
|
108
|
+
|
|
109
|
+
publish:
|
|
110
|
+
needs: validate
|
|
111
|
+
if: needs.validate.outputs.bumped == 'true'
|
|
112
|
+
runs-on: ubuntu-latest
|
|
113
|
+
environment: pypi
|
|
114
|
+
permissions:
|
|
115
|
+
contents: read
|
|
116
|
+
id-token: write
|
|
117
|
+
steps:
|
|
118
|
+
- uses: actions/download-artifact@v4
|
|
119
|
+
with:
|
|
120
|
+
name: distributions
|
|
121
|
+
path: dist/
|
|
122
|
+
|
|
123
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
## Project
|
|
4
|
+
|
|
5
|
+
`h2hdb-ingest` is the filesystem-facing ingest service for H2HDB. Python must
|
|
6
|
+
be run through `uv run --no-sync`, using the repository-local virtual
|
|
7
|
+
environment. The supported Python version is defined by `requires-python` in
|
|
8
|
+
`pyproject.toml`.
|
|
9
|
+
|
|
10
|
+
## Ownership Boundary
|
|
11
|
+
|
|
12
|
+
This repository owns:
|
|
13
|
+
|
|
14
|
+
- filesystem scanning and gallery discovery;
|
|
15
|
+
- `galleryinfo.txt` parsing orchestration;
|
|
16
|
+
- source hashing and deduplication policy;
|
|
17
|
+
- CBZ creation, rebuilding, and final reconciliation;
|
|
18
|
+
- the resident ingest loop and ingest-lease heartbeat orchestration.
|
|
19
|
+
|
|
20
|
+
Keep the src layout (`src/h2hdb_ingest`); the public import package is
|
|
21
|
+
`h2hdb_ingest`. Do not move the package to the repository root merely to match
|
|
22
|
+
an older project.
|
|
23
|
+
|
|
24
|
+
The `h2hdb` core package exclusively owns connectors, transactions, database
|
|
25
|
+
schema and migrations, durable queues, token-fenced coordination, catalog
|
|
26
|
+
repositories, and catalog publication. Depend only on core public interfaces;
|
|
27
|
+
do not import connector or repository internals and do not add schema here.
|
|
28
|
+
Catalog publication must follow successful final CBZ reconciliation.
|
|
29
|
+
Prepare new CBZ files without overwriting currently published artifacts. Before
|
|
30
|
+
entering the revision transaction, durably protect every selected artifact from
|
|
31
|
+
pruning. After commit, promote those files to published state. Retain immutable
|
|
32
|
+
published and commit-ambiguous protected artifacts for historical revisions;
|
|
33
|
+
prune only abandoned staging artifacts. Consumer startup performs a
|
|
34
|
+
compatibility check and must never migrate core schema.
|
|
35
|
+
|
|
36
|
+
CBZ operation uses two distinct, non-nested roots. `artifact_store_path` owns
|
|
37
|
+
content-addressed immutable artifacts and reconciliation state for OPDS;
|
|
38
|
+
`cbz_path` owns only the current friendly-file projection for Komga. Update the
|
|
39
|
+
Komga projection only after catalog publication succeeds, and remove only paths
|
|
40
|
+
recorded as managed in artifact-store state. Never replace or delete an unknown
|
|
41
|
+
file in the Komga root.
|
|
42
|
+
|
|
43
|
+
Every CBZ-enabled publisher must use the same shared `artifact_store_path` as
|
|
44
|
+
its coordination domain. Acquire the artifact-store publication flock before
|
|
45
|
+
the core database gate and hold it from immediately before catalog publication
|
|
46
|
+
through projection finalization; never acquire these locks in the reverse
|
|
47
|
+
order. The OS releases the flock if a process exits, and the durable pending
|
|
48
|
+
projection journal is the source of truth for crash recovery.
|
|
49
|
+
Persist the current projection's artifact identity and regular-file stat
|
|
50
|
+
signature. Skip a projection copy only when both still match; any external
|
|
51
|
+
mutation must be reverified with an atomic copy.
|
|
52
|
+
|
|
53
|
+
## Environment and Commands
|
|
54
|
+
|
|
55
|
+
This is a standalone repository, not a uv workspace. `uv.lock` is ignored and
|
|
56
|
+
must not become a build, test, or runtime input.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv venv --python 3.14
|
|
60
|
+
uv pip install -e ".[dev]"
|
|
61
|
+
uv run --no-sync ruff check .
|
|
62
|
+
uv run --no-sync black --check .
|
|
63
|
+
uv run --no-sync mypy src tests
|
|
64
|
+
uv run --no-sync pytest
|
|
65
|
+
uv run --no-sync python -m build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If the environment is damaged, run `scripts/rebuild-env.sh`.
|
|
69
|
+
|
|
70
|
+
## Shared Finalization
|
|
71
|
+
|
|
72
|
+
The repository keeps provider-neutral hooks in `scripts/hooks/`.
|
|
73
|
+
|
|
74
|
+
- After changing Python, run `bash scripts/hooks/finalize-python.sh`.
|
|
75
|
+
- After changing Markdown, run `bash scripts/hooks/finalize-markdown.sh`.
|
|
76
|
+
|
|
77
|
+
Do not create agent-specific copies of these implementations.
|
|
78
|
+
|
|
79
|
+
## Design and Compatibility
|
|
80
|
+
|
|
81
|
+
The project is pre-1.0. Prefer clear responsibility boundaries and the cleanest
|
|
82
|
+
end state over compatibility shims or deprecated aliases. Follow SOLID
|
|
83
|
+
principles and keep network waits or filesystem work outside core database
|
|
84
|
+
transactions and the database gate.
|
|
85
|
+
|
|
86
|
+
`CLAUDE.md` documents the same repository rules. Keep both files synchronized
|
|
87
|
+
when changing workflow, ownership, testing, or tooling conventions.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides repository guidance to Claude Code and other coding agents.
|
|
4
|
+
|
|
5
|
+
## Communication
|
|
6
|
+
|
|
7
|
+
- Claude 必須以繁體中文回答所有對話內容;程式碼、指令、檔名與專有名詞維持原文。
|
|
8
|
+
|
|
9
|
+
## Project
|
|
10
|
+
|
|
11
|
+
`h2hdb-ingest` is the filesystem-facing ingest service for H2HDB. It owns
|
|
12
|
+
gallery discovery, `galleryinfo.txt` parsing orchestration, hashing,
|
|
13
|
+
deduplication policy, CBZ creation and reconciliation, and the resident ingest
|
|
14
|
+
loop with its ingest-lease heartbeat.
|
|
15
|
+
|
|
16
|
+
Keep the modern src layout at `src/h2hdb_ingest`; consumers still import
|
|
17
|
+
`h2hdb_ingest`. Do not flatten it to match older repositories.
|
|
18
|
+
|
|
19
|
+
The `h2hdb` core package exclusively owns database connectors, transactions,
|
|
20
|
+
schema migrations, durable queues, token fencing, catalog repositories, and
|
|
21
|
+
catalog publication. Use only core public interfaces. Never import connector or
|
|
22
|
+
repository internals, create schema, or publish catalog data before final CBZ
|
|
23
|
+
reconciliation succeeds.
|
|
24
|
+
|
|
25
|
+
Prepare content-addressed CBZ artifacts without overwriting currently published
|
|
26
|
+
files. Durably protect selected artifacts before the catalog revision
|
|
27
|
+
transaction, then promote them to published state after commit. Retain immutable
|
|
28
|
+
published and commit-ambiguous protected artifacts for historical revisions;
|
|
29
|
+
prune only abandoned staging artifacts. Startup performs only the core schema
|
|
30
|
+
compatibility check and must not run migrations.
|
|
31
|
+
|
|
32
|
+
CBZ operation uses two distinct, non-nested roots. `artifact_store_path` owns
|
|
33
|
+
content-addressed immutable artifacts and reconciliation state for OPDS;
|
|
34
|
+
`cbz_path` owns only the current friendly-file projection for Komga. Update the
|
|
35
|
+
Komga projection only after catalog publication succeeds, and remove only paths
|
|
36
|
+
recorded as managed in artifact-store state. Never replace or delete an unknown
|
|
37
|
+
file in the Komga root.
|
|
38
|
+
|
|
39
|
+
Every CBZ-enabled publisher must use the same shared `artifact_store_path` as
|
|
40
|
+
its coordination domain. Acquire the artifact-store publication flock before
|
|
41
|
+
the core database gate and hold it from immediately before catalog publication
|
|
42
|
+
through projection finalization; never acquire these locks in the reverse
|
|
43
|
+
order. The OS releases the flock if a process exits, and the durable pending
|
|
44
|
+
projection journal is the source of truth for crash recovery.
|
|
45
|
+
Persist the current projection's artifact identity and regular-file stat
|
|
46
|
+
signature. Skip a projection copy only when both still match; any external
|
|
47
|
+
mutation must be reverified with an atomic copy.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
This is an independent repository and not part of a uv workspace. `uv.lock` is
|
|
52
|
+
ignored and must not be used by build, test, or runtime workflows. Always run
|
|
53
|
+
Python through `uv run --no-sync` after installing the editable environment.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv venv --python 3.14
|
|
57
|
+
uv pip install -e ".[dev]"
|
|
58
|
+
uv run --no-sync ruff check .
|
|
59
|
+
uv run --no-sync black --check .
|
|
60
|
+
uv run --no-sync mypy src tests
|
|
61
|
+
uv run --no-sync pytest
|
|
62
|
+
uv run --no-sync python -m build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use `scripts/rebuild-env.sh` to recreate a damaged environment.
|
|
66
|
+
|
|
67
|
+
## Shared Finalization
|
|
68
|
+
|
|
69
|
+
Provider-neutral hooks live in `scripts/hooks/` and are shared by humans and
|
|
70
|
+
all coding agents.
|
|
71
|
+
|
|
72
|
+
- After Python changes, run `bash scripts/hooks/finalize-python.sh`.
|
|
73
|
+
- After Markdown changes, run `bash scripts/hooks/finalize-markdown.sh`.
|
|
74
|
+
|
|
75
|
+
Do not duplicate their implementation under an agent-specific directory.
|
|
76
|
+
|
|
77
|
+
## Design
|
|
78
|
+
|
|
79
|
+
This project is pre-1.0. Prefer the cleanest architecture over compatibility
|
|
80
|
+
shims and deprecated aliases. Follow SOLID principles. Keep filesystem work,
|
|
81
|
+
compression, sleeps, and network operations outside core transactions and the
|
|
82
|
+
database gate.
|
|
83
|
+
|
|
84
|
+
Keep this document synchronized with `AGENTS.md` whenever ownership, workflow,
|
|
85
|
+
testing, or tooling conventions change.
|