hugo_deployment/tests/test_hugo_version_pins.py
Neil Borromeo 366b448bb0
All checks were successful
Publish Hugo Deployment Image / publish (push) Successful in 54s
Pin checksummed Hugo download in shared workflow
The deploy job downloads Hugo Extended 0.165.0 from the GitHub release,
verifies its SHA-256 and runs it from RUNNER_TEMP instead of the runner's
auto-refreshing snap. Deploys fail before rsync if hugo is a different
version, not Extended, or not the pinned binary.
2026-10-19 15:48:34 +08:00

226 lines
9.2 KiB
Python

import os
import re
import subprocess
import tempfile
import textwrap
import unittest
from pathlib import Path
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
INFRASTRUCTURE_ROOT = REPOSITORY_ROOT.parent / "hetzner.develton.com"
EXPECTED_HUGO_VERSION = "0.165.0"
EXPECTED_HUGO_SHA256 = "f43494894cdf4a8630a201d5c828051c77f523cc66bb3938b30806835470ac20"
def read(path: Path) -> str:
return path.read_text(encoding="utf-8")
def workflow_run_block(workflow: str, step_name: str) -> str:
match = re.search(
rf"^ - name: {re.escape(step_name)}\n"
r" run: \|\n"
r"(?P<body>(?: .*\n)+)",
workflow,
re.MULTILINE,
)
if match is None:
raise AssertionError(f"Workflow step not found: {step_name}")
return textwrap.dedent(match.group("body"))
class HugoVersionPinTests(unittest.TestCase):
def test_legacy_image_uses_expected_hugo_version(self) -> None:
dockerfile = read(REPOSITORY_ROOT / "Dockerfile")
match = re.search(r"^ARG HUGO_VERSION=(\S+)$", dockerfile, re.MULTILINE)
self.assertIsNotNone(match, "Dockerfile must declare HUGO_VERSION")
self.assertEqual(EXPECTED_HUGO_VERSION, match.group(1))
self.assertIn("hugo_extended_${HUGO_VERSION}_", dockerfile)
def test_shared_workflow_pins_and_validates_extended_hugo(self) -> None:
workflow = read(
REPOSITORY_ROOT / ".forgejo" / "workflows" / "deploy-hugo-site.yml"
)
self.assertRegex(
workflow,
re.compile(
rf'^ HUGO_VERSION: ["\']{re.escape(EXPECTED_HUGO_VERSION)}["\']$',
re.MULTILINE,
),
)
self.assertIn(f' HUGO_SHA256: "{EXPECTED_HUGO_SHA256}"', workflow)
path = re.search(r"^ PATH: (.+)$", workflow, re.MULTILINE)
self.assertIsNotNone(path)
self.assertNotIn("/snap/hugo", path.group(1))
checkout_position = workflow.find("- name: Checkout repository")
install_position = workflow.find("- name: Install pinned Hugo Extended")
self.assertGreaterEqual(checkout_position, 0)
self.assertGreater(install_position, checkout_position)
installation = workflow_run_block(workflow, "Install pinned Hugo Extended")
self.assertIn("sha256sum --check --strict", installation)
self.assertIn("GITHUB_PATH", installation)
validation_position = workflow.find("- name: Validate Hugo version")
deploy_position = workflow.find("- name: Run deploy script")
self.assertGreater(validation_position, install_position)
self.assertGreater(deploy_position, validation_position)
validation = workflow_run_block(workflow, "Validate Hugo version")
self.assertIn("hugo version", validation)
self.assertIn("exit 1", validation)
def test_shared_workflow_version_guard_fails_closed(self) -> None:
workflow = read(
REPOSITORY_ROOT / ".forgejo" / "workflows" / "deploy-hugo-site.yml"
)
validation = workflow_run_block(workflow, "Validate Hugo version")
with tempfile.TemporaryDirectory() as temporary_directory:
hugo_directory = Path(temporary_directory) / f"hugo-{EXPECTED_HUGO_VERSION}"
hugo_directory.mkdir()
fake_hugo = hugo_directory / "hugo"
fake_hugo.write_text(
"#!/bin/sh\nprintf '%s\\n' \"$FAKE_HUGO_VERSION_OUTPUT\"\n",
encoding="utf-8",
)
fake_hugo.chmod(0o755)
environment = os.environ.copy()
environment["PATH"] = f"{hugo_directory}:{environment['PATH']}"
environment["RUNNER_TEMP"] = temporary_directory
environment["HUGO_VERSION"] = EXPECTED_HUGO_VERSION
accepted_outputs = (
(
"hugo v0.165.0+extended linux/amd64 "
"BuildDate=unknown VendorInfo=gohugoio"
),
(
"hugo v0.165.0-67fb390c6309dc2b8361e714fc5d71e3e4d625cf"
"+extended linux/amd64 BuildDate=unknown VendorInfo=snap:0.165.0"
),
)
for accepted_output in accepted_outputs:
with self.subTest(accepted_output=accepted_output):
environment["FAKE_HUGO_VERSION_OUTPUT"] = accepted_output
accepted = subprocess.run(
["bash", "-c", validation],
env=environment,
capture_output=True,
text=True,
check=False,
)
self.assertEqual(0, accepted.returncode, accepted.stderr)
rejected_outputs = (
"hugo v0.164.0+extended linux/amd64 BuildDate=unknown",
"hugo v0.165.1+extended linux/amd64 BuildDate=unknown",
"hugo v0.165.1-abcdef+extended linux/amd64 BuildDate=unknown",
"hugo v0.165.0 linux/amd64 BuildDate=unknown",
"hugo v0.165.0-abcdef linux/amd64 BuildDate=unknown",
)
for rejected_output in rejected_outputs:
with self.subTest(rejected_output=rejected_output):
environment["FAKE_HUGO_VERSION_OUTPUT"] = rejected_output
rejected = subprocess.run(
["bash", "-c", validation],
env=environment,
capture_output=True,
text=True,
check=False,
)
self.assertNotEqual(0, rejected.returncode)
other_directory = Path(temporary_directory) / "other"
other_directory.mkdir()
other_hugo = other_directory / "hugo"
other_hugo.write_text(fake_hugo.read_text(), encoding="utf-8")
other_hugo.chmod(0o755)
environment["FAKE_HUGO_VERSION_OUTPUT"] = accepted_outputs[0]
environment["PATH"] = f"{other_directory}:{environment['PATH']}"
rejected = subprocess.run(
["bash", "-c", validation],
env=environment,
capture_output=True,
text=True,
check=False,
)
self.assertNotEqual(0, rejected.returncode)
self.assertIn("Expected hugo at", rejected.stderr)
def test_install_step_rejects_checksum_mismatch(self) -> None:
workflow = read(
REPOSITORY_ROOT / ".forgejo" / "workflows" / "deploy-hugo-site.yml"
)
installation = workflow_run_block(workflow, "Install pinned Hugo Extended")
with tempfile.TemporaryDirectory() as temporary_directory:
temporary_path = Path(temporary_directory)
fake_bin = temporary_path / "bin"
fake_bin.mkdir()
fake_curl = fake_bin / "curl"
fake_curl.write_text(
'#!/bin/sh\n'
'while [ "$#" -gt 0 ]; do\n'
' if [ "$1" = "--output" ]; then\n'
' printf junk > "$2"\n'
' exit 0\n'
' fi\n'
' shift\n'
'done\n'
'exit 1\n',
encoding="utf-8",
)
fake_curl.chmod(0o755)
github_path = temporary_path / "github_path"
github_path.touch()
environment = os.environ.copy()
environment.update(
PATH=f"{fake_bin}:{environment['PATH']}",
RUNNER_TEMP=temporary_directory,
GITHUB_PATH=str(github_path),
HUGO_VERSION=EXPECTED_HUGO_VERSION,
HUGO_SHA256=EXPECTED_HUGO_SHA256,
)
rejected = subprocess.run(
["bash", "-c", installation],
env=environment,
capture_output=True,
text=True,
check=False,
)
self.assertNotEqual(0, rejected.returncode)
self.assertIn("FAILED", rejected.stdout)
self.assertEqual("", github_path.read_text())
self.assertFalse((temporary_path / f"hugo-{EXPECTED_HUGO_VERSION}" / "hugo").exists())
def test_infrastructure_scaffold_uses_expected_hugo_version(self) -> None:
bootstrap = read(
INFRASTRUCTURE_ROOT
/ "ansible"
/ "tasks"
/ "bootstrap-hugo-site-from-deployment.yml"
)
self.assertIn(f'HUGO_VERSION: "{EXPECTED_HUGO_VERSION}"', bootstrap)
self.assertNotRegex(bootstrap, r'HUGO_VERSION: "(?!0\.165\.0)[^"]+"')
def test_readme_documents_supported_sites_and_version_guarantee(self) -> None:
readme = read(REPOSITORY_ROOT / "README.md")
expected_sites = (
"neilborromeo.com",
"boholtoday.com",
"boholcentral.com",
"komsysnet.com",
"thefreelancemastery.com",
)
documented_sites = re.findall(r"^- `([^`]+)`$", readme, re.MULTILINE)
self.assertEqual(list(expected_sites), documented_sites[: len(expected_sites)])
self.assertIn("Hugo Extended 0.165.0", readme)
self.assertIn("fails before deployment", readme)
if __name__ == "__main__":
unittest.main()