fix: network property means any network reach, not only HTTP (#144)
CI / verify (push) Successful in 1m13s
Release / release (push) Successful in 35s

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:
torben committed 2026-09-26 15:19:58 +02:00
1 parent 16c911fca5
commit 906d63fae2
9 files changed
+86 -24

No files matched your search

+4 -4
View File
@@ -1611,7 +1611,7 @@ Fetch `<remote>/<branch>` and bring the local branch up to date with it.
- idempotent: yes
- atomic: No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure
- budget: counted
- network: no
- network: yes
- gates: rebase-review
**EXAMPLES**
@@ -1664,7 +1664,7 @@ Reconcile with `<remote>/<branch>`, then stage all changes, commit, and push.
- idempotent: no
- atomic: No - sequential git operations, but every gate runs before staging
- budget: counted
- network: no
- network: yes
- gates: mass-update, publish-remote, rebase-review
**EXAMPLES**
@@ -2566,7 +2566,7 @@ Ask the origin's release feed whether a newer stack exists.
**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`.
- 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).
- 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").
@@ -3042,7 +3042,7 @@ Take a stack update into a private instance's branch, machinery only.
- idempotent: no
- atomic: **No** - can leave an open, uncommitted merge behind on refusal after fetching
- budget: counted
- network: no
- network: yes
**EXAMPLES**
+9
View File
@@ -51,6 +51,15 @@ class Budget(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"
NO = "no"
+2
View File
@@ -1010,6 +1010,7 @@ def apply_reconcile(outcome: ReconcileOutcome, remote: str, branch: str, command
idempotent=cli_contract.Idempotent.YES,
atomic="No - fetch, then at most one merge/rebase attempt, aborted cleanly on failure",
budget=cli_contract.Budget.COUNTED,
network=cli_contract.Network.YES,
gates=("rebase-review",),
),
notes=(
@@ -1089,6 +1090,7 @@ def sync_command(
idempotent=cli_contract.Idempotent.NO,
atomic="No - sequential git operations, but every gate runs before staging",
budget=cli_contract.Budget.COUNTED,
network=cli_contract.Network.YES,
gates=("mass-update", "publish-remote", "rebase-review"),
),
notes=(
+1
View File
@@ -249,6 +249,7 @@ def _merge_success_message(
idempotent=cli_contract.Idempotent.NO,
atomic="**No** - can leave an open, uncommitted merge behind on refusal after fetching",
budget=cli_contract.Budget.COUNTED,
network=cli_contract.Network.YES,
),
notes=(
"Refuses on a dirty working tree, a merge already in progress, or a remote that does "
+11 -11
View File
@@ -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
overwrites, it falls back to the release feed, because otherwise the command
can never answer there - not today and not after any future release.
- `version check` and that fallback are the only two network calls in
`wikitool`, and neither is implicit: `check` exists for the call, `notes`
announces the URL on stderr before asking and takes `--offline`. Both need
no key, both time out, and a feed that cannot be reached is reported as an
error rather than silently answered as "up to date" or "no notes".
- `version check` and that fallback both reach the release feed, and neither
is implicit: `check` exists for the call, `notes` announces the URL on
stderr before asking and takes `--offline`. Both need no key, both time
out, and a feed that cannot be reached is reported as an error rather than
silently answered as "up to date" or "no notes".
"""
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` "
"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).",
"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.
The only networked command in `wikitool`. Exits 1 if the feed cannot be
reached or does not answer with a release - an unreachable feed is not the
same answer as "up to date", and must never be reported as one."""
The only command whose whole job is the network call. Exits 1 if the feed
cannot be reached or does not answer with a release - an unreachable feed
is not the same answer as "up to date", and must never be reported as
one."""
import os
try:
+26
View File
@@ -285,6 +285,32 @@ def test_every_gated_record_shows_its_re_run_after_exit_42():
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) ---
+7 -7
View File
@@ -348,13 +348,13 @@ def fetch_latest(
"""The version the release feed reports as latest.
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
`wikitool` that talks to a remote host, and only two commands reach it:
`version check`, whose whole job it is, and `version notes` on a
*distributed* instance, whose local `CHANGES.md` is a stub with no entry to
print (see `fetch_latest_notes`). Neither is implicit - `check` exists for
the call, and `notes` announces the URL it is asking before it asks, on
stderr, and takes `--offline` for a caller that wants none of it.
and every test - can run without a network. This is the one place that
talks to the **release feed**, and only two commands reach it: `version
check`, whose whole job it is, and `version notes` on a *distributed*
instance, whose local `CHANGES.md` is a stub with no entry to print (see
`fetch_latest_notes`). Neither is implicit - `check` exists for the call,
and `notes` announces the URL it is asking before it asks, on stderr, and
takes `--offline` for a caller that wants none of it.
"""
fetch = fetcher or _urlopen_fetch
try: