fix: network property means any network reach, not only HTTP (#144)
Files changed: - CHANGES.md - VERSION - tools/CONTRACT.md - tools/chemenu/cli_contract.py - tools/chemenu/commands/git_publish.py - tools/chemenu/commands/upstream_cmd.py - tools/chemenu/commands/version_cmd.py - tools/chemenu/tests/test_cli.py - tools/chemenu/version.py
This commit is contained in:
1 parent
16c911fca5
commit
906d63fae2
9 files changed
+86
-24
No files matched your search
+25
-1
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7.1.0-beta.22 - 2026-09-26 - fail() prints the command's ON FAILURE lines on stderr
|
## 7.1.0-beta.23 - 2026-09-26 - network: property defined; sync, publish and upstream merge marked networked
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
- Command records, Private instances group: one line per cause, examples, prohibitions
|
- Command records, Private instances group: one line per cause, examples, prohibitions
|
||||||
- Command records, Instance health group: one bullet per check, examples
|
- Command records, Instance health group: one bullet per check, examples
|
||||||
- Command records: NOTES is always a tuple of bullets; every record's examples are tested
|
- Command records: NOTES is always a tuple of bullets; every record's examples are tested
|
||||||
|
- network: property defined; sync, publish and upstream merge marked networked
|
||||||
<!-- /wikitool:bumps -->
|
<!-- /wikitool:bumps -->
|
||||||
|
|
||||||
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
|
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
|
||||||
@@ -358,6 +359,29 @@ byte-identical to before. `cli.py`'s and `_util.py`'s lookup of the running comm
|
|||||||
`cli_contract` path is now one shared function, `cli_contract.path_of`, in place of a private
|
`cli_contract` path is now one shared function, `cli_contract.path_of`, in place of a private
|
||||||
copy that used to live only in `cli.py`.
|
copy that used to live only in `cli.py`.
|
||||||
|
|
||||||
|
### network: Eigenschaft definiert; sync, publish und upstream merge als netzwerkend markiert
|
||||||
|
|
||||||
|
Gitea #144: `network:` blieb undefiniert und stand mit dem Code sowie mit sich selbst im
|
||||||
|
Widerspruch. `sync` und `publish` (`git_publish.py`) sowie `upstream merge` (`upstream_cmd.py`)
|
||||||
|
sprechen ein Git-Remote an (`fetch`, `ls-remote`, `push`), trugen aber `network: no`; `upstream
|
||||||
|
verify` liest nur bereits geholte Refs und bleibt zu Recht bei `no`. Gleichzeitig behauptete
|
||||||
|
`version check`s Datensatz, mit `version notes` eines von nur zwei netzwerkenden Kommandos in
|
||||||
|
`wikitool` zu sein - während `review`, `task new`/`task list`/`task close` und `doctor` seit
|
||||||
|
Gitea #121 ebenfalls `network: yes` tragen. Drei weitere Docstrings wiederholten dieselbe
|
||||||
|
Ausschließlichkeit: der Modul- und der Befehls-Docstring von `version_cmd.py` sowie
|
||||||
|
`fetch_latest`s Docstring in `version.py`, dessen Behauptung „the one place that talks to a
|
||||||
|
remote host" auch unter der bisherigen, engen Lesart falsch war, da `tasks/caldav.py` und
|
||||||
|
`tasks/superproductivity.py` ebenfalls per `urllib` sprechen.
|
||||||
|
|
||||||
|
Entschieden (Operator, 2026-09-26): `network:` meint jeden Netzzugriff, gleich ob per HTTP aus
|
||||||
|
`wikitool` selbst oder per Git-Operation gegen ein Remote - maßgeblich ist, was möglich ist,
|
||||||
|
nicht was im Normalfall passiert, und ein Git-Aufruf, der nur lokale Refs liest, zählt nicht.
|
||||||
|
Diese Bedeutung steht jetzt im Docstring von `cli_contract.Network`. `sync`, `publish` und
|
||||||
|
`upstream merge` tragen `network: yes`; alle vier überzähligen bzw. falschen Textstellen sind
|
||||||
|
korrigiert, ohne eine neue Zahl zu behaupten. Ein neuer Test schreibt die Menge der `network:
|
||||||
|
yes`-Pfade explizit fest, damit ein künftiger Wechsel eine bewusste Teständerung ist statt
|
||||||
|
stillen Auseinanderlaufens.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
|
## 7.0.0 - 2026-09-22 - Task-Tracker-Anbindung: Vorhaben als Seitenart, Verpflichtungsschicht, Weekly Review als Read-Time-Join
|
||||||
|
|||||||
+4
-4
@@ -1611,7 +1611,7 @@ Fetch `<remote>/<branch>` and bring the local branch up to date with it.
|
|||||||
- idempotent: yes
|
- idempotent: yes
|
||||||
- atomic: No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure
|
- atomic: No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure
|
||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: yes
|
||||||
- gates: rebase-review
|
- gates: rebase-review
|
||||||
|
|
||||||
**EXAMPLES**
|
**EXAMPLES**
|
||||||
@@ -1664,7 +1664,7 @@ Reconcile with `<remote>/<branch>`, then stage all changes, commit, and push.
|
|||||||
- idempotent: no
|
- idempotent: no
|
||||||
- atomic: No - sequential git operations, but every gate runs before staging
|
- atomic: No - sequential git operations, but every gate runs before staging
|
||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: yes
|
||||||
- gates: mass-update, publish-remote, rebase-review
|
- gates: mass-update, publish-remote, rebase-review
|
||||||
|
|
||||||
**EXAMPLES**
|
**EXAMPLES**
|
||||||
@@ -2566,7 +2566,7 @@ Ask the origin's release feed whether a newer stack exists.
|
|||||||
**NOTES**
|
**NOTES**
|
||||||
|
|
||||||
- Asks the release feed for its latest release and compares it with `VERSION`: `state` is `current`, `update`, `migration` (the step crosses a compatibility boundary) or `ahead`.
|
- Asks the release feed for its latest release and compares it with `VERSION`: `state` is `current`, `update`, `migration` (the step crosses a compatibility boundary) or `ahead`.
|
||||||
- One of the **two** commands in `wikitool` that make a network call, and the only one whose whole job it is - `version notes` is the other, and only on a distributed instance.
|
- The only command whose whole job is the network call - `version notes` reaches the same feed too, but only as a fallback on a distributed instance.
|
||||||
- Never reached implicitly from another command, needs no key, and times out after `--timeout` seconds (default 10).
|
- Never reached implicitly from another command, needs no key, and times out after `--timeout` seconds (default 10).
|
||||||
- The feed is `--url`, else `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the built-in origin. `$WIKITOOL_UPDATE_TOKEN` is only needed if that feed is not readable anonymously.
|
- The feed is `--url`, else `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the built-in origin. `$WIKITOOL_UPDATE_TOKEN` is only needed if that feed is not readable anonymously.
|
||||||
- For `update` or `migration` it prints that applying the release is a separate, manual step (`INSTALL.md` § "Eine Instanz aktualisieren").
|
- For `update` or `migration` it prints that applying the release is a separate, manual step (`INSTALL.md` § "Eine Instanz aktualisieren").
|
||||||
@@ -3042,7 +3042,7 @@ Take a stack update into a private instance's branch, machinery only.
|
|||||||
- idempotent: no
|
- idempotent: no
|
||||||
- atomic: **No** - can leave an open, uncommitted merge behind on refusal after fetching
|
- atomic: **No** - can leave an open, uncommitted merge behind on refusal after fetching
|
||||||
- budget: counted
|
- budget: counted
|
||||||
- network: no
|
- network: yes
|
||||||
|
|
||||||
**EXAMPLES**
|
**EXAMPLES**
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,15 @@ class Budget(str, Enum):
|
|||||||
|
|
||||||
|
|
||||||
class Network(str, Enum):
|
class Network(str, Enum):
|
||||||
|
"""Whether at least one path through this command can reach an endpoint outside this
|
||||||
|
checkout - an HTTP call `wikitool` makes itself (release feed, task tracker), or a git
|
||||||
|
operation against a remote (`fetch`, `ls-remote`, `push`). `YES` if either kind is
|
||||||
|
possible, not only if it is the usual case: a flag that avoids it (`--offline`,
|
||||||
|
`--no-fetch`) or a configuration with no remote endpoint (a markdown tracker, a remote
|
||||||
|
pointed at a local path) does not turn the value back to `NO` - what matters is whether the
|
||||||
|
command can stall or fail on an unreachable network, not what it does on a good day. A git
|
||||||
|
call that only reads refs already on disk (`rev-parse`, `rev-list`, `remote get-url`) is not
|
||||||
|
a network access on its own."""
|
||||||
YES = "yes"
|
YES = "yes"
|
||||||
NO = "no"
|
NO = "no"
|
||||||
|
|
||||||
|
|||||||
@@ -1010,6 +1010,7 @@ def apply_reconcile(outcome: ReconcileOutcome, remote: str, branch: str, command
|
|||||||
idempotent=cli_contract.Idempotent.YES,
|
idempotent=cli_contract.Idempotent.YES,
|
||||||
atomic="No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure",
|
atomic="No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
|
network=cli_contract.Network.YES,
|
||||||
gates=("rebase-review",),
|
gates=("rebase-review",),
|
||||||
),
|
),
|
||||||
notes=(
|
notes=(
|
||||||
@@ -1089,6 +1090,7 @@ def sync_command(
|
|||||||
idempotent=cli_contract.Idempotent.NO,
|
idempotent=cli_contract.Idempotent.NO,
|
||||||
atomic="No - sequential git operations, but every gate runs before staging",
|
atomic="No - sequential git operations, but every gate runs before staging",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
|
network=cli_contract.Network.YES,
|
||||||
gates=("mass-update", "publish-remote", "rebase-review"),
|
gates=("mass-update", "publish-remote", "rebase-review"),
|
||||||
),
|
),
|
||||||
notes=(
|
notes=(
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ def _merge_success_message(
|
|||||||
idempotent=cli_contract.Idempotent.NO,
|
idempotent=cli_contract.Idempotent.NO,
|
||||||
atomic="**No** - can leave an open, uncommitted merge behind on refusal after fetching",
|
atomic="**No** - can leave an open, uncommitted merge behind on refusal after fetching",
|
||||||
budget=cli_contract.Budget.COUNTED,
|
budget=cli_contract.Budget.COUNTED,
|
||||||
|
network=cli_contract.Network.YES,
|
||||||
),
|
),
|
||||||
notes=(
|
notes=(
|
||||||
"Refuses on a dirty working tree, a merge already in progress, or a remote that does "
|
"Refuses on a dirty working tree, a merge already in progress, or a remote that does "
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ number means, and `instructions/dev/version-parts.md` for the candidate model):
|
|||||||
*distributed* instance, whose `CHANGES.md` is a stub `dist upgrade` never
|
*distributed* instance, whose `CHANGES.md` is a stub `dist upgrade` never
|
||||||
overwrites, it falls back to the release feed, because otherwise the command
|
overwrites, it falls back to the release feed, because otherwise the command
|
||||||
can never answer there - not today and not after any future release.
|
can never answer there - not today and not after any future release.
|
||||||
- `version check` and that fallback are the only two network calls in
|
- `version check` and that fallback both reach the release feed, and neither
|
||||||
`wikitool`, and neither is implicit: `check` exists for the call, `notes`
|
is implicit: `check` exists for the call, `notes` announces the URL on
|
||||||
announces the URL on stderr before asking and takes `--offline`. Both need
|
stderr before asking and takes `--offline`. Both need no key, both time
|
||||||
no key, both time out, and a feed that cannot be reached is reported as an
|
out, and a feed that cannot be reached is reported as an error rather than
|
||||||
error rather than silently answered as "up to date" or "no notes".
|
silently answered as "up to date" or "no notes".
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -157,9 +157,8 @@ def show_command(
|
|||||||
"Asks the release feed for its latest release and compares it with `VERSION`: `state` "
|
"Asks the release feed for its latest release and compares it with `VERSION`: `state` "
|
||||||
"is `current`, `update`, `migration` (the step crosses a compatibility boundary) or "
|
"is `current`, `update`, `migration` (the step crosses a compatibility boundary) or "
|
||||||
"`ahead`.",
|
"`ahead`.",
|
||||||
"One of the **two** commands in `wikitool` that make a network call, and the only one "
|
"The only command whose whole job is the network call - `version notes` reaches the "
|
||||||
"whose whole job it is - `version notes` is the other, and only on a distributed "
|
"same feed too, but only as a fallback on a distributed instance.",
|
||||||
"instance.",
|
|
||||||
"Never reached implicitly from another command, needs no key, and times out after "
|
"Never reached implicitly from another command, needs no key, and times out after "
|
||||||
"`--timeout` seconds (default 10).",
|
"`--timeout` seconds (default 10).",
|
||||||
"The feed is `--url`, else `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the "
|
"The feed is `--url`, else `$WIKITOOL_UPDATE_URL`, else the release stamp's, else the "
|
||||||
@@ -205,9 +204,10 @@ def check_command(
|
|||||||
):
|
):
|
||||||
"""Ask the origin's release feed whether a newer stack exists.
|
"""Ask the origin's release feed whether a newer stack exists.
|
||||||
|
|
||||||
The only networked command in `wikitool`. Exits 1 if the feed cannot be
|
The only command whose whole job is the network call. Exits 1 if the feed
|
||||||
reached or does not answer with a release - an unreachable feed is not the
|
cannot be reached or does not answer with a release - an unreachable feed
|
||||||
same answer as "up to date", and must never be reported as one."""
|
is not the same answer as "up to date", and must never be reported as
|
||||||
|
one."""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -285,6 +285,32 @@ def test_every_gated_record_shows_its_re_run_after_exit_42():
|
|||||||
assert missing == []
|
assert missing == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_network_yes_is_exactly_the_commands_that_can_reach_outside_this_checkout():
|
||||||
|
"""Gitea #144: `network:` means *any* reach outside this checkout - an HTTP call
|
||||||
|
`wikitool` makes itself, or a git operation against a remote (fetch/ls-remote/push) -
|
||||||
|
not only the two `version_cmd.py` used to claim exclusivity for. Pinned as an explicit
|
||||||
|
set so a command gaining or losing that reach is a deliberate edit here, not a silent
|
||||||
|
drift between the property and what the command actually does."""
|
||||||
|
expected = {
|
||||||
|
"sync",
|
||||||
|
"publish",
|
||||||
|
"upstream merge",
|
||||||
|
"version check",
|
||||||
|
"version notes",
|
||||||
|
"review",
|
||||||
|
"task new",
|
||||||
|
"task list",
|
||||||
|
"task close",
|
||||||
|
"doctor",
|
||||||
|
}
|
||||||
|
actual = {
|
||||||
|
path
|
||||||
|
for path, rec in cli_contract.all_records().items()
|
||||||
|
if rec.properties.network is cli_contract.Network.YES
|
||||||
|
}
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
# --- fail()'s ON FAILURE hint, through two real commands (Gitea #143) ---
|
# --- fail()'s ON FAILURE hint, through two real commands (Gitea #143) ---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -348,13 +348,13 @@ def fetch_latest(
|
|||||||
"""The version the release feed reports as latest.
|
"""The version the release feed reports as latest.
|
||||||
|
|
||||||
The network call sits behind `fetcher` so every caller above this line -
|
The network call sits behind `fetcher` so every caller above this line -
|
||||||
and every test - can run without a network. This is the one place in
|
and every test - can run without a network. This is the one place that
|
||||||
`wikitool` that talks to a remote host, and only two commands reach it:
|
talks to the **release feed**, and only two commands reach it: `version
|
||||||
`version check`, whose whole job it is, and `version notes` on a
|
check`, whose whole job it is, and `version notes` on a *distributed*
|
||||||
*distributed* instance, whose local `CHANGES.md` is a stub with no entry to
|
instance, whose local `CHANGES.md` is a stub with no entry to print (see
|
||||||
print (see `fetch_latest_notes`). Neither is implicit - `check` exists for
|
`fetch_latest_notes`). Neither is implicit - `check` exists for the call,
|
||||||
the call, and `notes` announces the URL it is asking before it asks, on
|
and `notes` announces the URL it is asking before it asks, on stderr, and
|
||||||
stderr, and takes `--offline` for a caller that wants none of it.
|
takes `--offline` for a caller that wants none of it.
|
||||||
"""
|
"""
|
||||||
fetch = fetcher or _urlopen_fetch
|
fetch = fetcher or _urlopen_fetch
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in new issue
Block a user