fix: publish --path commits the uncommitted generated files outside its path, so the pushed catalog and log match its pages (#182)
Files changed: - CHANGES.md - VERSION - tools/CONTRACT.md - tools/chemenu/commands/git_publish.py - tools/chemenu/tests/test_git_publish.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
1 parent
fa106eeda3
commit
a3fc8f6873
5 files changed
+190
-15
No files matched your search
@@ -1898,3 +1898,123 @@ def test_an_index_md_under_raw_still_goes_to_review_when_both_sides_change_it(re
|
||||
|
||||
assert outcome.status == "needs-review"
|
||||
assert outcome.overlap_files == ["raw/repos/project/INDEX.md"]
|
||||
|
||||
|
||||
# --- publish --path and the uncommitted generated files outside it ---
|
||||
|
||||
PROTOCOLS = "kb/concepts/protocols"
|
||||
|
||||
|
||||
def test_publish_path_commits_the_generated_files_the_reconcile_regenerated(wiki_repo, capsys):
|
||||
"""The pushed tip is consistent: its catalog and provenance are what a rebuild of it writes,
|
||||
and its log carries every local entry once. Nothing generated stays behind uncommitted."""
|
||||
writer = _clone_writer(wiki_repo)
|
||||
_writer_ingest(writer, "Writer")
|
||||
_ingest(wiki_repo, "Local")
|
||||
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS])
|
||||
|
||||
check = _check_tree(wiki_repo)
|
||||
assert (check / f"{PROTOCOLS}/Local.md").is_file()
|
||||
assert _logged_titles(check) == ["Base", "Writer", "Local"]
|
||||
_assert_generated_current(check)
|
||||
assert _status(wiki_repo) == ""
|
||||
out = capsys.readouterr().out
|
||||
assert "generated file(s) outside --path" in out
|
||||
assert "kb/index.md" in out and "kb/log.md" in out
|
||||
|
||||
|
||||
def test_publish_path_commits_the_generated_files_without_a_reconcile_too(wiki_repo, capsys):
|
||||
"""Nothing came in, so nothing was regenerated - the catalog and log the ingest itself wrote
|
||||
still go out with its page, not one batch later."""
|
||||
_ingest(wiki_repo, "Local")
|
||||
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS])
|
||||
|
||||
check = _check_tree(wiki_repo)
|
||||
assert _logged_titles(check) == ["Base", "Local"]
|
||||
_assert_generated_current(check)
|
||||
assert _status(wiki_repo) == ""
|
||||
assert "generated file(s) outside --path" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_publish_path_after_sync_commits_what_the_sync_regenerated(wiki_repo):
|
||||
writer = _clone_writer(wiki_repo)
|
||||
_writer_ingest(writer, "Writer")
|
||||
_ingest(wiki_repo, "Local")
|
||||
_sync()
|
||||
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS])
|
||||
|
||||
check = _check_tree(wiki_repo)
|
||||
assert _logged_titles(check) == ["Base", "Writer", "Local"]
|
||||
_assert_generated_current(check)
|
||||
assert _status(wiki_repo) == ""
|
||||
|
||||
|
||||
def test_publish_path_leaves_every_other_file_outside_it_alone(wiki_repo):
|
||||
_ingest(wiki_repo, "Local")
|
||||
(wiki_repo / "README.md").write_text("init\nedited, not in scope\n", encoding="utf-8")
|
||||
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS])
|
||||
|
||||
committed = _git(wiki_repo, "show", "--name-only", "--format=", "HEAD").stdout.split()
|
||||
assert "README.md" not in committed and "kb/index.md" in committed
|
||||
assert _status(wiki_repo) == " M README.md\n"
|
||||
|
||||
|
||||
def test_publish_path_names_nothing_when_the_generated_files_are_already_in_scope(
|
||||
wiki_repo, capsys
|
||||
):
|
||||
writer = _clone_writer(wiki_repo)
|
||||
_writer_ingest(writer, "Writer")
|
||||
_ingest(wiki_repo, "Local")
|
||||
|
||||
_publish(message="ingest: Local", path=["kb"])
|
||||
|
||||
check = _check_tree(wiki_repo)
|
||||
assert _logged_titles(check) == ["Base", "Writer", "Local"]
|
||||
_assert_generated_current(check)
|
||||
assert _status(wiki_repo) == ""
|
||||
assert "outside --path" not in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_publish_path_keeps_its_token_and_rerun_line_when_generated_files_join(
|
||||
wiki_repo, monkeypatch
|
||||
):
|
||||
"""The added files are never counted, so the clearance a user gives for `--path <X>` is the
|
||||
one the gate asks for - with or without them - and it publishes."""
|
||||
from chemenu.telemetry import reader
|
||||
|
||||
writer = _clone_writer(wiki_repo)
|
||||
_writer_ingest(writer, "Writer")
|
||||
for i in range(9):
|
||||
_write_concept(wiki_repo, f"Local{i}")
|
||||
_ingest(wiki_repo, "Local")
|
||||
token_before = _token_for(wiki_repo, paths=[PROTOCOLS])
|
||||
shown = {}
|
||||
real_message = git_publish.clearance_message
|
||||
|
||||
def spy(changes, threshold, token, rerun, *args, **kwargs):
|
||||
shown.update(changes=[c.path for c in changes], rerun=rerun)
|
||||
return real_message(changes, threshold, token, rerun, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(git_publish, "clearance_message", spy)
|
||||
|
||||
with pytest.raises(typer.Exit) as excinfo:
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS])
|
||||
|
||||
assert excinfo.value.exit_code == EXIT_NEEDS_CLEARANCE
|
||||
assert "kb/index.md" in shown["changes"] # the generated files did join the changeset
|
||||
refused = [r for r in reader.read_trace("test-session") if r["event"] == "gate.refused"]
|
||||
token = refused[-1]["attrs"]["token"]
|
||||
assert token == token_before == _token_for(wiki_repo, paths=[PROTOCOLS])
|
||||
assert shown["rerun"] == rerun_command(token, "ingest: Local", True, 10, "origin", "main",
|
||||
[PROTOCOLS])
|
||||
|
||||
_publish(message="ingest: Local", path=[PROTOCOLS], confirm=token)
|
||||
|
||||
check = _check_tree(wiki_repo)
|
||||
assert _logged_titles(check) == ["Base", "Writer", "Local"]
|
||||
_assert_generated_current(check)
|
||||
assert _status(wiki_repo) == ""
|
||||
Reference in new issue
Block a user