Pin the website publication toolchain
This commit is contained in:
@@ -11,6 +11,7 @@ from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import shlex
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -107,6 +108,12 @@ def publish_catalog_candidate(
|
||||
steps: list[CatalogPublishStep] = []
|
||||
notes: list[str] = []
|
||||
blockers: list[str] = []
|
||||
resolved_npm = npm
|
||||
if build_web:
|
||||
try:
|
||||
resolved_npm = _trusted_npm_command(npm)
|
||||
except (OSError, RuntimeError) as exc:
|
||||
blockers.append(f"website build command is not trusted: {exc}")
|
||||
|
||||
if not candidate_catalog.exists():
|
||||
blockers.append(f"candidate catalog is missing: {candidate_catalog}")
|
||||
@@ -399,9 +406,18 @@ def publish_catalog_candidate(
|
||||
CatalogPublishStep(
|
||||
id="build-web",
|
||||
title="Build website",
|
||||
detail="Run the website build after copying catalog files.",
|
||||
detail=(
|
||||
"Run the website build after copying catalog files with "
|
||||
f"{resolved_npm}."
|
||||
),
|
||||
command=shlex.join(
|
||||
[npm, "--prefix", str(resolved_web_root), "run", "build"]
|
||||
[
|
||||
resolved_npm,
|
||||
"--prefix",
|
||||
str(resolved_web_root),
|
||||
"run",
|
||||
"build",
|
||||
]
|
||||
),
|
||||
mutating=True,
|
||||
status="planned" if not apply else "blocked" if blockers else "pending",
|
||||
@@ -559,7 +575,7 @@ def publish_catalog_candidate(
|
||||
]
|
||||
if build_web:
|
||||
run_checked(
|
||||
[npm, "--prefix", str(resolved_web_root), "run", "build"],
|
||||
[resolved_npm, "--prefix", str(resolved_web_root), "run", "build"],
|
||||
cwd=resolved_web_root,
|
||||
)
|
||||
completed_steps = [
|
||||
@@ -2009,10 +2025,14 @@ def git_success(path: Path, *args: str) -> bool:
|
||||
|
||||
|
||||
def run_checked(args: list[str], *, cwd: Path) -> None:
|
||||
if not args or not Path(args[0]).is_absolute():
|
||||
raise RuntimeError("publication build command must be an absolute path")
|
||||
environment = _sanitized_git_environment()
|
||||
environment["PATH"] = f"{Path(args[0]).parent}:/usr/bin:/bin"
|
||||
result = subprocess.run(
|
||||
args,
|
||||
cwd=cwd,
|
||||
env=_sanitized_git_environment(),
|
||||
env=environment,
|
||||
check=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
@@ -2030,3 +2050,34 @@ def run_checked(args: list[str], *, cwd: Path) -> None:
|
||||
output=result.stdout,
|
||||
stderr=result.stderr,
|
||||
)
|
||||
|
||||
|
||||
def _trusted_npm_command(value: str) -> str:
|
||||
candidate = Path(value)
|
||||
if not candidate.is_absolute():
|
||||
if candidate.name != value:
|
||||
raise RuntimeError(
|
||||
"npm executable must be an absolute path or the bare name npm"
|
||||
)
|
||||
located = shutil.which(value, path="/usr/bin:/bin")
|
||||
if not located:
|
||||
raise RuntimeError("npm executable is unavailable on the fixed system path")
|
||||
candidate = Path(located)
|
||||
candidate = Path(os.path.abspath(candidate))
|
||||
permitted_owners = {0, os.geteuid()}
|
||||
issue = _trusted_runtime_executable_issue(
|
||||
candidate,
|
||||
permitted_owners=permitted_owners,
|
||||
label="npm executable",
|
||||
)
|
||||
if issue:
|
||||
raise RuntimeError(issue)
|
||||
node = candidate.parent / "node"
|
||||
issue = _trusted_runtime_executable_issue(
|
||||
node,
|
||||
permitted_owners=permitted_owners,
|
||||
label="Node.js executable",
|
||||
)
|
||||
if issue:
|
||||
raise RuntimeError(issue)
|
||||
return str(candidate)
|
||||
|
||||
Reference in New Issue
Block a user