Skip to content

emrg: the release tag has a maintained push path when git-over-https is down - #2796

Open
argszero wants to merge 2 commits into
masterfrom
fix/tag-push-through-the-api
Open

argszero wants to merge 2 commits into
masterfrom
fix/tag-push-through-the-api

Conversation

@argszero

Copy link
Copy Markdown
Owner

Closes #2795

Release stage 2 — publishing the annotated tag — had no maintained path when git-over-https is
unreachable, so releasing under an outage meant writing a throwaway script from notes each time.
scripts/push-branch-from-api.py exists to end exactly that flow for the push direction, but it
only pushed refs/heads. This adds --tag beside --branch.

What changed

  • push_tag + parse_tag + _remote_tag_target in scripts/push-branch-from-api.py, on the
    same api() seam the branch path uses. The tag object is created with POST /git/tags, carrying
    the tagger's own instant and offset parsed from the local object — a tag stamped "now" is a
    different object from the one that was cut — then refs/tags/<name> is created with
    POST /git/refs. Creating that ref is what fires build-release.yml, so this is the whole of
    stage 2.
  • Four refusals before the first request, each because the alternative costs a build: a
    lightweight tag (the workflow's own verify-tag job would refuse it only after a four-platform
    build), a tag absent locally, an already-published tag naming a different commit (a version number
    is not a branch to rewrite), and neither-or-both of --branch/--tag. Re-running stage 2 after a
    dropped connection is a no-op, not an error.
  • The ref is read back and the run fails if it does not name the object that was created.

How it is verified

tests/test_push_branch_from_api.py grows from 14 to 19 tests, using the fake GitHub the branch
tests already use — extended here with POST /git/tags, GET /git/tags/<sha> and the singular
GET /git/ref/tags/<name>, recomputing tag shas with real git hash-object so the payload's
byte-exactness is the thing measured. The offset assertion reads the posted payload
(2023-11-15T06:13:20+08:00 from a +0800 author env), not the object rebuilt from it.

Each refusal was armed in both directions — mutated, its own test watched red, restored:

mutation the test that reddened
annotated-tag refusal removed test_a_lightweight_tag_is_refused_before_any_request
ref created before the tag object test_push_tag_creates_the_tag_object_before_the_ref
published-tag refusal removed test_push_tag_refuses_to_move_a_published_tag
neither-or-both check removed test_the_cli_takes_exactly_one_of_branch_or_tag

The module is 19 passed; tests/test_script_output_ascii.py 7 passed (the new output is ASCII, as
that guard requires) and scripts/check-undefined-names.py reads OK over 420 files.

What was not measured

The whole Python suite was not run for this branch, and this PR's CI is pending — the two legs
are the first reading of it. The tool itself was just used to publish this branch (remote
dfd4e346, content identical to the local tip), which exercises the branch path but not the new
tag path against the real API.

@argszero

Copy link
Copy Markdown
Owner Author

The test leg went red on the head this PR was opened from, and the cause was in this PR's own new test: tests/test_push_branch_from_api.py spawned the script with text=True and no encoding=, which tests/test_script_decode_is_locale_independent.py::test_every_text_mode_subprocess_pins_its_encoding refuses — a text-mode child decodes with the locale codec, so a non-UTF-8 host (cp936/GBK, cp1252) either raises or silently mojibakes correctly-UTF-8 output.

Fixed in 9b5fcc8d by pinning encoding="utf-8", errors="replace" on that call. Measured both ways on this tree:

  • with the pin: tests/test_script_decode_is_locale_independent.py + tests/test_push_branch_from_api.py — 38 passed;
  • with the pin removed: the guard reddens with exactly tests/test_push_branch_from_api.py:742 ... has no encoding= (child not statically known);
  • tree restored: 19 passed.

The other subprocess.run in the file (the git helper, line 52) passes bytes and takes input=, so it is not in the guard's class and was left as it is.

CI on the new head is pending; nothing here is claimed as green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

emrg: the release tag has no maintained push path when git-over-https is down

1 participant