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.
Files changed (32) hide show
  1. h2hdb_ingest-0.1.0/.github/workflows/publish.yml +123 -0
  2. h2hdb_ingest-0.1.0/.gitignore +13 -0
  3. h2hdb_ingest-0.1.0/AGENTS.md +87 -0
  4. h2hdb_ingest-0.1.0/CLAUDE.md +85 -0
  5. h2hdb_ingest-0.1.0/LICENSE +674 -0
  6. h2hdb_ingest-0.1.0/PKG-INFO +906 -0
  7. h2hdb_ingest-0.1.0/README.md +204 -0
  8. h2hdb_ingest-0.1.0/mypy.ini +18 -0
  9. h2hdb_ingest-0.1.0/pyproject.toml +58 -0
  10. h2hdb_ingest-0.1.0/scripts/bootstrap-catalog.py +7 -0
  11. h2hdb_ingest-0.1.0/scripts/hooks/finalize-markdown.sh +28 -0
  12. h2hdb_ingest-0.1.0/scripts/hooks/finalize-python.sh +26 -0
  13. h2hdb_ingest-0.1.0/scripts/rebuild-env.sh +17 -0
  14. h2hdb_ingest-0.1.0/src/h2hdb_ingest/__init__.py +32 -0
  15. h2hdb_ingest-0.1.0/src/h2hdb_ingest/__main__.py +66 -0
  16. h2hdb_ingest-0.1.0/src/h2hdb_ingest/bootstrap.py +58 -0
  17. h2hdb_ingest-0.1.0/src/h2hdb_ingest/cbz.py +1248 -0
  18. h2hdb_ingest-0.1.0/src/h2hdb_ingest/config.py +137 -0
  19. h2hdb_ingest-0.1.0/src/h2hdb_ingest/deduplication.py +164 -0
  20. h2hdb_ingest-0.1.0/src/h2hdb_ingest/models.py +94 -0
  21. h2hdb_ingest-0.1.0/src/h2hdb_ingest/naming.py +11 -0
  22. h2hdb_ingest-0.1.0/src/h2hdb_ingest/py.typed +1 -0
  23. h2hdb_ingest-0.1.0/src/h2hdb_ingest/resident.py +240 -0
  24. h2hdb_ingest-0.1.0/src/h2hdb_ingest/scanner.py +258 -0
  25. h2hdb_ingest-0.1.0/src/h2hdb_ingest/service.py +259 -0
  26. h2hdb_ingest-0.1.0/tests/test_bootstrap_catalog.py +79 -0
  27. h2hdb_ingest-0.1.0/tests/test_cbz_current_view.py +900 -0
  28. h2hdb_ingest-0.1.0/tests/test_cli.py +97 -0
  29. h2hdb_ingest-0.1.0/tests/test_config.py +142 -0
  30. h2hdb_ingest-0.1.0/tests/test_ingest.py +1193 -0
  31. h2hdb_ingest-0.1.0/tests/test_package.py +21 -0
  32. 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,13 @@
1
+ .DS_Store
2
+ .coverage
3
+ .mypy_cache/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ __pycache__/
8
+ *.egg-info/
9
+ *.py[cod]
10
+ build/
11
+ dist/
12
+ htmlcov/
13
+ uv.lock
@@ -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.