mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 04:06:42 +00:00
1d57e22926 raised web-app-docs' mermaid_version from 11.4.1 to 12.1.0 and left
mermaid_sha256 describing the 11.4.1 asset. The Dockerfile fetches that asset
with ADD --checksum, so every round that deploys docs died in the build with
#7 ERROR: digest mismatch sha256:6484afc3...: sha256:a43bc1af...
which names neither the pin nor the role. Seven of the eleven failures of run
37440146966 are this one line. The pin now carries the digest the CDN actually
serves, measured three ways that agree: a host curl, the new helper, and
buildkit's own computed value in those jobs.
The separation is the real defect, so the updater closes it. A version pin may
now declare checksum_key and checksum_url beside its type, and after writing a
new version apply_updates refetches that URL with {version} substituted, hashes
it, and replaces the value on the checksum_key line of the same entity block.
A pin that declares neither behaves as before and performs no request.
invalid_declarations rejects the two ways the declaration could silently do
nothing: a checksum_key naming a key the entity does not carry, and a
checksum_url without the placeholder.
source.py stood at 496 of the 500-line cap, so the three line locators move to
utils/update/pins.py and checksum.rewrite reuses them instead of repeating the
block-scoped search.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
"""Locate a declared pin inside the file that carries it.
|
|
|
|
A pin lives either in the block of its entity in ``meta/services.yml`` or at
|
|
the root of a ``meta/addons/<id>.yml``. The updater has to find its line to
|
|
rewrite exactly that one, so the search is textual: parsing and re-emitting
|
|
the YAML would reformat every other line of the file.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
|
|
def key_line(lines: list[str], entity: str, key: str) -> int | None:
|
|
"""Return the 1-indexed line of ``key`` inside the block of ``entity``.
|
|
|
|
Args:
|
|
lines: lines of the file that carries the pin.
|
|
entity: top-level key whose block holds the pin.
|
|
key: pinned key.
|
|
"""
|
|
inside = False
|
|
for number, line in enumerate(lines, start=1):
|
|
if re.match(rf"^{re.escape(entity)}\s*:", line):
|
|
inside = True
|
|
continue
|
|
if inside and line and not line[0].isspace():
|
|
inside = False
|
|
if inside and re.match(rf"^\s+{re.escape(key)}\s*:", line):
|
|
return number
|
|
return None
|
|
|
|
|
|
def top_level_line(lines: list[str], key: str) -> int | None:
|
|
"""Return the 1-indexed line of ``key`` at the root of an addon file.
|
|
|
|
Args:
|
|
lines: lines of the file that carries the pin.
|
|
key: pinned key.
|
|
"""
|
|
for number, line in enumerate(lines, start=1):
|
|
if re.match(rf"^{re.escape(key)}\s*:", line):
|
|
return number
|
|
return None
|
|
|
|
|
|
def archive_index(lines: list[str]) -> int | None:
|
|
"""Return the 0-indexed line of ``config.archive`` in an addon file.
|
|
|
|
Args:
|
|
lines: lines of the file that carries the pin.
|
|
"""
|
|
for index, line in enumerate(lines):
|
|
if re.match(r"^\s+archive\s*:", line):
|
|
return index
|
|
return None
|