mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-08-10 16:37:34 +00:00
fix(ci): harden publishing and validate examples
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate the book's canonical container examples with their native tools."""
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FIXTURE_ROOT = ROOT / "examples" / "validated"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Check:
|
||||
name: str
|
||||
executable: str
|
||||
command: List[str]
|
||||
probe: Optional[List[str]] = None
|
||||
|
||||
|
||||
CHECKS = (
|
||||
Check(
|
||||
"docker compose",
|
||||
"docker",
|
||||
[
|
||||
"docker",
|
||||
"compose",
|
||||
"-f",
|
||||
str(FIXTURE_ROOT / "compose" / "compose.yaml"),
|
||||
"config",
|
||||
"--quiet",
|
||||
],
|
||||
["docker", "compose", "version"],
|
||||
),
|
||||
Check(
|
||||
"docker buildx",
|
||||
"docker",
|
||||
[
|
||||
"docker",
|
||||
"buildx",
|
||||
"build",
|
||||
"--check",
|
||||
"--file",
|
||||
str(FIXTURE_ROOT / "dockerfile" / "Dockerfile"),
|
||||
str(FIXTURE_ROOT / "dockerfile"),
|
||||
],
|
||||
["docker", "buildx", "version"],
|
||||
),
|
||||
Check(
|
||||
"kubeconform",
|
||||
"kubeconform",
|
||||
[
|
||||
"kubeconform",
|
||||
"-strict",
|
||||
"-summary",
|
||||
"-kubernetes-version",
|
||||
"1.31.0",
|
||||
str(FIXTURE_ROOT / "kubernetes" / "web.yaml"),
|
||||
],
|
||||
),
|
||||
Check(
|
||||
"actionlint",
|
||||
"actionlint",
|
||||
["actionlint", str(FIXTURE_ROOT / "github-actions" / "validate.yml")],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def run_command(command):
|
||||
return subprocess.run(command, cwd=ROOT, capture_output=True, text=True)
|
||||
|
||||
|
||||
def unavailable(check, require_tools, detail):
|
||||
status = "UNAVAILABLE" if require_tools else "SKIP"
|
||||
print(f"[{status}] {check.name}: {detail}")
|
||||
return require_tools
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--require-tools",
|
||||
action="store_true",
|
||||
help="fail instead of skipping when a validator is unavailable (for CI)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
failed = False
|
||||
|
||||
for check in CHECKS:
|
||||
if shutil.which(check.executable) is None:
|
||||
failed = unavailable(check, args.require_tools, f"{check.executable} not found") or failed
|
||||
continue
|
||||
|
||||
if check.probe is not None:
|
||||
probe = run_command(check.probe)
|
||||
if probe.returncode != 0:
|
||||
detail = (probe.stderr or probe.stdout or "plugin probe failed").strip()
|
||||
failed = unavailable(check, args.require_tools, detail) or failed
|
||||
continue
|
||||
|
||||
result = run_command(check.command)
|
||||
if result.returncode != 0:
|
||||
print(f"[FAIL] {check.name}")
|
||||
print((result.stderr or result.stdout).strip())
|
||||
failed = True
|
||||
continue
|
||||
|
||||
print(f"[PASS] {check.name}")
|
||||
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user