Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/requirements/ci-geopandas.lock
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ cachecontrol==0.14.4 \
--hash=sha256:b7ac014ff72ee199b5f8af1de29d60239954f223e948196fa3d84adaffc71d2b \
--hash=sha256:e6220afafa4c22a47dd0badb319f84475d79108100d04e26e8542ef7d3ab05a1
# via pip-audit
certifi==2026.7.22 \
--hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \
--hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55
certifi==2026.10.19 \
--hash=sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae \
--hash=sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b
# via
# httpcore
# httpx
Expand Down
6 changes: 3 additions & 3 deletions .github/requirements/ci.lock
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ cachecontrol==0.14.4 \
--hash=sha256:b7ac014ff72ee199b5f8af1de29d60239954f223e948196fa3d84adaffc71d2b \
--hash=sha256:e6220afafa4c22a47dd0badb319f84475d79108100d04e26e8542ef7d3ab05a1
# via pip-audit
certifi==2026.7.22 \
--hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \
--hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55
certifi==2026.10.19 \
--hash=sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae \
--hash=sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b
# via
# httpcore
# httpx
Expand Down
22 changes: 13 additions & 9 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ pip install "honua-sdk[grpc,geopandas,raster]"
pip install honua-admin
```

For unreleased development work, install from a clone instead:

This is an alternative to the PyPI path, not the next release-install step. The
commands below install the checkout's version into the current environment and
can replace published packages. An unqualified clone follows the current default
branch, which may contain a newer version than the release you are verifying.
Use the governed published package versions for release qualification; a
successful source build does not establish that those package artifacts work.

The commands above are the release install. They use the configured package
index and version constraints and leave the admitted published package in place.

For unreleased development work, install from a clone in a separate
environment instead. This is not the next release-install step. The commands
below install the checkout's version into the current environment and replace
published packages. An unqualified clone follows the current default branch,
which may contain a newer version than the release you are verifying. Release
qualification refuses every direct-origin install, including one whose version
string matches the admitted package. A successful source build does not
establish that those package artifacts work.

<!-- doc-run: skip reason="Development source install replaces the admitted published package. Release qualification refuses direct-origin installs even when the version matches." -->
```bash
git clone https://github.com/honua-io/honua-sdk-python.git
cd honua-sdk-python
Expand Down
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@ pip install "honua-sdk[grpc,geopandas,raster]" # + optional extras
pip install honua-admin # control-plane (admin) client
```

For development, install from a source checkout instead:

These commands install the checkout's version into the current Python environment
and can replace packages installed from PyPI above. Choose this path for source
development, rather than running it after the published install to verify a
release. A clone without a revision follows the repository's current default
branch; it does not select the source revision of this document or a released
package. Source installation still requires compatible dependencies and build
tooling.

The commands above are the release install. They use the configured package
index and version constraints and leave the admitted published package in place.

A source checkout is a separate development environment, not the next step
after that install. The commands below install the checkout's version into the
current environment and replace the published package. An unqualified clone
follows the default branch, not this document's revision and not the published
artifact. Release qualification refuses every direct-origin install, including
one whose version string matches the admitted package. A successful source
build does not establish published wheel identity. Use a separate environment
for that work; the contributor setup is also in [AGENTS.md](AGENTS.md).

<!-- doc-run: skip reason="Development source install replaces the admitted published package. Release qualification refuses direct-origin installs even when the version matches." -->
```bash
git clone https://github.com/honua-io/honua-sdk-python.git
cd honua-sdk-python
Expand Down Expand Up @@ -283,11 +286,14 @@ at [honua.io](https://honua.io) and

## Development

Run these from a clone of this repository (see [Install](#install)). Besides
Python 3.11+, the test suite needs Node.js with `node` on `PATH`: the `honua`
CLI forwards `honua admin ...` to the JavaScript CLI, and `tests/test_cli.py`
checks that forwarding.
Lint, type-check, and tests run from a clone. Besides Python 3.11+, the test
suite needs Node.js with `node` on `PATH`: the `honua` CLI forwards
`honua admin ...` to the JavaScript CLI, and `tests/test_cli.py` checks that
forwarding. The editable installs below replace the published package, so they
belong in a development environment, not in the environment used to verify a
release. The same contributor setup is in [AGENTS.md](AGENTS.md).

<!-- doc-run: skip reason="Editable source install replaces the admitted published package. Release qualification refuses direct-origin installs even when the version matches." -->
```bash
# Editable install of both packages with extras
pip install -e "packages/honua-sdk[grpc,geopandas]"
Expand Down
69 changes: 69 additions & 0 deletions tests/test_published_getting_started_install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Getting-started release installs stay on the admitted published package.

Source-checkout fences remain in README and INSTALL so the development-source
producer can still capture them. The executable-docs gate must not run those
fences: a direct-origin install replaces the admitted package, and release
qualification refuses that origin even when the version string matches.
"""
from __future__ import annotations

from pathlib import Path
import re

from scripts import development_source_inputs as inputs
from scripts import python_document_inputs as documentation


ROOT = Path(__file__).resolve().parents[1]
DOCUMENTS = ("README.md", "INSTALL.md")
FENCE = re.compile(r"^```([^\n]*)\n(.*?)^```[ \t]*$", re.MULTILINE | re.DOTALL)
SKIP = re.compile(
r"<!--\s*doc-run:\s*skip\s+reason=\"[^\"]+\"\s*-->\s*$",
re.MULTILINE,
)
DIRECT_ORIGIN = re.compile(
r"git\s+clone\b|pip\s+install\s+-e\b|pip\s+install\s+\.?/?packages/|pip\s+install\s+\"\./packages/",
)


def _release_fences(text: str) -> list[tuple[str, str, str]]:
"""Return (info, preceding, code) for each fence."""
found = []
cursor = 0
for match in FENCE.finditer(text):
found.append((match.group(1), text[cursor:match.start()], match.group(2)))
cursor = match.end()
return found


def test_release_install_fences_use_the_published_package() -> None:
for name in DOCUMENTS:
text = (ROOT / name).read_text(encoding="utf-8")
release_installs = []
for info, preceding, code in _release_fences(text):
if info.strip().split()[0] not in {"bash", "sh", "shell"}:
continue
if SKIP.search(preceding):
continue
assert DIRECT_ORIGIN.search(code) is None, name
if "pip install" in code and "honua-sdk" in code:
release_installs.append(code)
assert release_installs, name
assert any(re.search(r"^pip install honua-sdk\b", line) or 'pip install "honua-sdk' in line
for code in release_installs for line in code.splitlines()), name


def test_source_checkout_fences_stay_captured_and_are_not_release_steps() -> None:
source = {"files": {name: {"gitBlob": "0" * 40} for name in DOCUMENTS}}
# plan() reads with the platform default encoding. Match that text so a
# Windows decode of the em dash does not look like a different fence.
texts = {name: (ROOT / name).read_text() for name in DOCUMENTS}
planned = documentation.plan(inputs, ROOT, source)
by_document: dict[str, int] = {}
for item in planned:
by_document[item["document"]] = by_document.get(item["document"], 0) + 1
matched = [row for row in _release_fences(texts[item["document"]]) if row[2] == item["blockBytes"]]
assert matched, item["document"]
assert SKIP.search(matched[0][1]), item["document"]
assert by_document["INSTALL.md"] >= 1
assert by_document["README.md"] >= 2
Loading