Milestone 318 step 8 — extension CalVer, and the #3190 mitigation's first main build #241
@@ -34,6 +34,15 @@ is genuinely the commit those paths last changed in.
|
||||
emit. Two nearly-identical formats are more dangerous than two obviously
|
||||
different ones, and the only thing keeping them identical is a test.
|
||||
|
||||
**The extension is the one exception, and it is a rendering exception only.**
|
||||
AMO's version grammar forbids a leading zero, so the extension emits the same
|
||||
numbers unpadded — `2026.8.29.201` where the family says `2026.08.29.0201`
|
||||
(#3138, milestone 318 step 8). Rule 148 defines comparison as numeric per
|
||||
dot-segment, under which the two are equal, so this is pinned in both
|
||||
directions below: the extension must satisfy AMO's grammar, and every artifact
|
||||
must derive the same NUMBERS its own commit stamps. An exception left as "the
|
||||
extension is different" would drift into being differently different.
|
||||
|
||||
The identity-TAG tests this file used to hold are gone with the tag. There is
|
||||
no longer a `CHANNELLED` list to drift (the channel is which tag you inspect),
|
||||
and no `identity` subcommand to refuse an unqualified call.
|
||||
@@ -57,6 +66,26 @@ _REVISION = re.compile(r"^[0-9a-f]{12}$")
|
||||
# YYYY.MM.DD.HHMM, every segment zero-padded to its full width.
|
||||
_VERSION = re.compile(r"^\d{4}\.\d{2}\.\d{2}\.\d{4}$")
|
||||
|
||||
# The artifacts that cannot use the padded rendering. Exactly one, and the
|
||||
# reason is external: `packaging.sh` derives the extension's version and AMO
|
||||
# refuses to sign a padded one.
|
||||
AMO_UNPADDED = frozenset({"extension"})
|
||||
|
||||
# Mozilla's published grammar for addons.mozilla.org, transcribed from MDN's
|
||||
# manifest.json/version page. A segment is the single digit `0` or starts 1-9,
|
||||
# and there are at most four. This is the constraint the exception exists for,
|
||||
# so it is what the exception is tested against — `2026.08.29.0201` fails it.
|
||||
_AMO = re.compile(r"^(0|[1-9][0-9]{0,8})(\.(0|[1-9][0-9]{0,8})){0,3}$")
|
||||
|
||||
# YYYY.M.D.HHMM — four segments, none of them zero-padded.
|
||||
_UNPADDED = re.compile(r"^\d{4}(\.(0|[1-9]\d*)){3}$")
|
||||
|
||||
|
||||
def segments(value: str) -> tuple[int, ...]:
|
||||
"""A version as the numbers it denotes, which is how rule 148 says to
|
||||
compare one. `2026.08.29.0201` and `2026.8.29.201` are one value here."""
|
||||
return tuple(int(part) for part in value.split("."))
|
||||
|
||||
|
||||
# Everything here goes through artifacts.sh rather than importing a sibling
|
||||
# test module. That is the interface build.yml actually calls, so the tests
|
||||
@@ -126,7 +155,7 @@ def test_revision_is_a_legal_label_value_and_is_stable(artifact):
|
||||
assert first == revision(artifact), "revision is not stable across calls"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("artifact", ARTIFACTS)
|
||||
@pytest.mark.parametrize("artifact", sorted(set(ARTIFACTS) - AMO_UNPADDED))
|
||||
def test_version_is_zero_padded_calver(artifact):
|
||||
"""The family shape, pinned.
|
||||
|
||||
@@ -148,6 +177,31 @@ def test_version_is_zero_padded_calver(artifact):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("artifact", sorted(AMO_UNPADDED))
|
||||
def test_the_unpadded_artifacts_derive_something_amo_will_sign(artifact):
|
||||
"""The other half of the family shape: the documented exception, tested
|
||||
against the constraint that justifies it rather than against itself.
|
||||
|
||||
A padded value passes `_UNPADDED` on any date with no leading zeros, so
|
||||
that pattern alone would let a regression sit unnoticed until the first
|
||||
single-digit month — at which point the failure is a burned AMO version,
|
||||
not a red lane. AMO's grammar is the assertion that fires immediately.
|
||||
"""
|
||||
value = artifacts("version", artifact).strip()
|
||||
assert _AMO.match(value), (
|
||||
f"{artifact} derives {value!r}, which AMO refuses: a segment must be "
|
||||
f"the single digit `0` or start 1-9, and there are at most four. "
|
||||
f"Almost certainly a zero-padded segment — the family pads and this "
|
||||
f"artifact must not (#3138). AMO 409s on re-signing, so a version it "
|
||||
f"rejects is burned."
|
||||
)
|
||||
assert _UNPADDED.match(value), (
|
||||
f"{artifact} derives {value!r}, which is not YYYY.M.D.HHMM. AMO would "
|
||||
f"also accept the pre-318 `1.0.<minutes>`, and that orders below every "
|
||||
f"ext-2026.* release already signed."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("artifact", ARTIFACTS)
|
||||
def test_version_and_revision_describe_the_same_commit(artifact):
|
||||
"""They are derived independently and must not be able to disagree.
|
||||
@@ -162,5 +216,21 @@ def test_version_and_revision_describe_the_same_commit(artifact):
|
||||
capture_output=True, text=True, check=True, cwd=ROOT,
|
||||
env={"TZ": "UTC", "PATH": os.environ.get("PATH", "")},
|
||||
).stdout.strip()
|
||||
assert artifacts("version", artifact).strip() == stamped
|
||||
derived = artifacts("version", artifact).strip()
|
||||
|
||||
# Compared as NUMBERS, which is how rule 148 defines comparison and the
|
||||
# only way one assertion can cover both renderings. This is what makes the
|
||||
# extension's exception cosmetic rather than semantic: it must denote
|
||||
# exactly the value its own commit stamps, whatever the padding.
|
||||
assert segments(derived) == segments(stamped), (
|
||||
f"{artifact} derives {derived!r}, but its newest shipped commit "
|
||||
f"{sha[:12]} is {stamped!r}. The instance would name one commit while "
|
||||
f"carrying another's bytes."
|
||||
)
|
||||
if artifact not in AMO_UNPADDED:
|
||||
assert derived == stamped, (
|
||||
f"{artifact} derives {derived!r} where the family shape is "
|
||||
f"{stamped!r} — same numbers, wrong rendering. Only the artifacts "
|
||||
f"in AMO_UNPADDED may differ here."
|
||||
)
|
||||
assert sha.startswith(revision(artifact))
|
||||
|
||||
Reference in New Issue
Block a user