diff --git a/README.md b/README.md index 501ba68..61c3db2 100644 --- a/README.md +++ b/README.md @@ -559,8 +559,8 @@ with no breaking change. From `v2` the module path carries `/vN`, so fences ship batched into as few majors as possible. [`testdata/shipped_shape.txt`](testdata/shipped_shape.txt) pins every path, each -category's format and the categories it reads, and each column's datatype and -nullability; a pull request that +template category's format, the categories each category reads, and each column's +datatype and nullability; a pull request that changes it or `data/` adds its `CHANGELOG.md` entry, which CI checks. A removed, renamed or retyped line is a major. diff --git a/release-tooling/publish_release.py b/release-tooling/publish_release.py index 40ff3e2..313b84e 100644 --- a/release-tooling/publish_release.py +++ b/release-tooling/publish_release.py @@ -12,7 +12,7 @@ import sys import urllib.error import urllib.request -HEADING = re.compile(r"^## \[([^\]]+)\].*?$\n(.*?)(?=^## \[|\Z)", re.M | re.S) +HEADING = re.compile(r"^## \[([^\]]+)\].*?$\n?(.*?)(?=^## \[|\Z)", re.M | re.S) def request(path: str, data: dict | None = None): @@ -45,7 +45,16 @@ def main() -> int: except urllib.error.HTTPError as e: if e.code != 404: raise - release = request("releases", {"body": body, "name": tag, "tag_name": tag, "target_commitish": os.environ["SHA"]}) + sha = os.environ["SHA"] + try: + at = request(f"tags/{tag}")["commit"]["sha"] + if at != sha: + print(f"{tag} exists at {at}, not {sha}; the version is burnt, bump the heading", file=sys.stderr) + return 1 + except urllib.error.HTTPError as e: + if e.code != 404: + raise + release = request("releases", {"body": body, "name": tag, "tag_name": tag, "target_commitish": sha}) print(release["html_url"]) return 0 diff --git a/shape_test.go b/shape_test.go index 04516d8..ca12de4 100644 --- a/shape_test.go +++ b/shape_test.go @@ -10,9 +10,7 @@ import ( const shapePin = "testdata/shipped_shape.txt" -// TestShippedShapeIsPinned pins what a version promises about the shipped data (see -// the README's Versioning): every path, each category's format and the categories it -// reads, and each record column's datatype and nullability. REPIN=1 rewrites the pin. +// REPIN=1 rewrites the pin. func TestShippedShapeIsPinned(t *testing.T) { f, err := New(WithSeed(1)) if err != nil { @@ -34,9 +32,6 @@ func TestShippedShapeIsPinned(t *testing.T) { } } -// shippedShape lists every path Fake accepts, one per line. A category carries the -// categories it references, a category-level template its format, and each of its -// columns its datatype and whether it may be null. func shippedShape(f *Generator) string { facts := map[string]string{} var walk func(prefix string, n node) @@ -108,10 +103,11 @@ func TestShippedShapeNamesReads(t *testing.T) { f := newGenerator(t, writeData(t, map[string]string{ "a": `{"format":"{x}","x":["{/b}",{"format":"{/c.v}","weight":2}]}`, "b": `"y"`, - "c": `{"format":"{v}","v":["z","w"]}`, - "d/pos": `["{/b}","q"]`, + "c": `{"format":"{v} {n}","n":[null,{"format":"{int(1,9)}","datatype":"integer"}],"v":["z","w"]}`, + "d/pos": `["{.q}","{/b}"]`, + "d/q": `"r"`, })) - want := "a\tformat \"{x}\"\treads b c\na.x\tstring\nb\tformat \"y\"\nc\tformat \"{v}\"\nc.v\tstring\nd.pos\treads b\n" + want := "a\tformat \"{x}\"\treads b c\na.x\tstring\nb\tformat \"y\"\nc\tformat \"{v} {n}\"\nc.n\tinteger null\nc.v\tstring\nd.pos\treads b d.q\nd.q\tformat \"r\"\n" if got := shippedShape(f); got != want { t.Fatalf("shippedShape =\n%s\nwant\n%s", got, want) }