Skip to content
Draft
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: 1 addition & 5 deletions luria/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
luria init scaffold the record into a project that has none
luria config write a starting luria.toml and stop, for editing
before anything is scaffolded
luria upgrade carry a record across a version boundary; every
command under it is temporary and says what has to
be true before it is deleted

Two more exist for CI, which is their only regular caller:

Expand All @@ -41,7 +38,7 @@
import fire

from . import (adr_index, collect, concretize, init, link_refs, lint, migrate,
new, remotes, repair, reports, site, upgrade)
new, remotes, repair, reports, site)

COMMANDS = {
"lint": lint.run,
Expand All @@ -57,7 +54,6 @@
"config": init.config_run,
"reports": reports.run,
"collect": collect.run,
"upgrade": upgrade.run,
}

# Run by CI on every push; runnable by hand, but nothing in the contributor
Expand Down
34 changes: 3 additions & 31 deletions luria/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ def check_frontmatter(errors: list[str]) -> None:
f"{rel}: status {status.value!r} is unchecked — the "
f"{scheme.prefix} scheme declares no `status` "
f"vocabulary, so any word passes and the absence looks "
f"exactly like a clean check "
f"(`luria upgrade statuses` writes one)")
f"exactly like a clean check — declare it with "
f"`[luria.schemes.{scheme.prefix}.fields.status]`")
# "Superseded names its successor" used to be a branch here.
# It is a `required_when` on the built-in field now, checked with
# every other obligation in `check_contracts` (ADR-071 stated
Expand Down Expand Up @@ -376,31 +376,10 @@ def scan_one(path) -> list[str]:
"source-mismatch", "source-unchecked",
"legacy-spellings", "narrow-titles", "stale-directives",
"template-drift", "broken-chains",
"one-sided-relations", "spent-upgrades",
"one-sided-relations",
"pending-documents", "unlinted-files", "workflow-temp-codes")


def spent_upgrades() -> list[str]:
"""Upgrades this record has already run.

A one-shot upgrade is temporary by construction, and the thing that
makes it *stay* temporary is being asked about. Once every record has
run one it is dead code that still has to be read, tested and
explained — so a record that no longer needs it says so, the way
`stale-directives` reports a directive that no longer suppresses
anything. One user's record saying it is not proof every record has,
which is why the row says "once every record has" rather than "now"."""
from . import upgrade
out = []
for name, entry in upgrade.SUNSET.items():
writes, lines, _ = upgrade._plan(current().root)
if not writes and not lines:
out.append(f"`luria upgrade {name}` has nothing left to do here "
f"— delete it once every record has run it "
f"({entry.sunset})")
return out


def status_sections() -> list[tuple[str, str, list[str]]]:
"""Every status finding, as (class, headline, detail lines) — computed
once, so the warning path and the `fail_on` path cannot disagree."""
Expand Down Expand Up @@ -585,13 +564,6 @@ def status_sections() -> list[tuple[str, str, list[str]]]:
"contract (a document copied from the form starts in the wrong "
"shape)", drift))

spent = spent_upgrades()
if spent:
sections.append((
"spent-upgrades",
f"{len(spent)} one-shot upgrade(s) this record no longer needs",
spent))

# A directive that silently does nothing is worse than no directive.
stale = ref_status.stale_annotations(result, docs) + stale_urls \
+ stale_targets + stale_sources + pins.flag_problems()
Expand Down
153 changes: 0 additions & 153 deletions luria/upgrade.py

This file was deleted.

13 changes: 13 additions & 0 deletions record/changelog.d/20260906-230545.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### Removed

- **`luria upgrade`, and the `statuses` upgrade under it.** It existed to
carry records across [#181](https://github.com/dmarx/luria/issues/181), and every record that predated [#181](https://github.com/dmarx/luria/issues/181) has run
it. A one-shot upgrade kept past its purpose is dead code that still has
to be read, tested and explained.
- Lint class `spent-upgrades`, which existed to ask this question. It asked
it, and this is the answer.

### Changed

- The finding for a scheme declaring no `status` vocabulary names the
declaration to write rather than a command that no longer exists.
3 changes: 1 addition & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import sys

from luria import (adr_index, cli, collect, concretize, init, link_refs,
lint, migrate, new, remotes, repair, reports, site, upgrade)
lint, migrate, new, remotes, repair, reports, site)


def test_every_command_is_registered():
Expand All @@ -24,7 +24,6 @@ def test_every_command_is_registered():
"migrate": migrate.run, "remotes": remotes.run,
"site": site.run, "init": init.run, "reports": reports.run,
"collect": collect.run, "config": init.config_run,
"upgrade": upgrade.run,
}


Expand Down
43 changes: 1 addition & 42 deletions tests/test_statuses.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,46 +739,5 @@ def test_a_scheme_without_a_status_vocabulary_fails_the_lint(
_value(tmp_path, 1)
errors: list[str] = []
lint.check_frontmatter(errors)
assert any("luria upgrade statuses" in e for e in errors), errors
assert any("declare it with" in e for e in errors), errors


def test_the_upgrade_satisfies_the_requirement(tmp_path, monkeypatch):
"""The fix ships with the break, and is the whole remedy: after it, the
record lints clean without anyone hand-editing config."""
from luria import upgrade
_project(tmp_path, monkeypatch)
_value(tmp_path, 1)
upgrade.run("statuses", root=str(tmp_path))
config.reset()
errors: list[str] = []
lint.check_frontmatter(errors)
lint.check_contracts(errors)
assert errors == [], errors


def test_the_upgrade_does_not_load_the_config_it_repairs(tmp_path, monkeypatch):
"""It has to run against a record the new version refuses to load, or it
is unrunnable in exactly the situation it exists for."""
from luria import upgrade
_project(tmp_path, monkeypatch)
(tmp_path / "luria.toml").write_text(
(tmp_path / "luria.toml").read_text()
+ '[luria.schemes.VP.fields.bogus]\nvocabulary = "nothing"\n')
config.reset()
with pytest.raises(ValueError):
config.current()
upgrade.run("statuses", root=str(tmp_path)) # must not raise
assert (tmp_path / "record" / "values.d" / "statuses.yaml").exists()


def test_a_spent_upgrade_says_it_can_be_deleted(tmp_path, monkeypatch):
"""The marker. An upgrade that has nothing left to do anywhere is dead
code that still has to be read and tested, so the lint raises the
question rather than waiting for someone to remember it — the posture
`stale-directives` already takes."""
from luria import upgrade
_project(tmp_path, monkeypatch)
upgrade.run("statuses", root=str(tmp_path))
config.reset()
rows = lint.spent_upgrades()
assert any("statuses" in r and "delete" in r for r in rows), rows
Loading