Files
StarPilot/scripts/lint/check_workflows.py
T
firestar5683 80fbb4f6db Feature registration
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Kacper Rączy <gfw.kra@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com>
Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

103 lines
4.2 KiB
Python

import hashlib
import io
import json
import os
import platform
import re
import subprocess
import sys
import tarfile
import tempfile
import urllib.request
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
VERSION = "1.7.12"
ARCHIVE_SHA256 = {
"darwin_amd64": "5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644",
"darwin_arm64": "aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f",
"linux_amd64": "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8",
"linux_arm64": "325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6",
}
# These two inherited jobs remain explicitly unavailable. No other diagnostic is waived.
DISABLED_JOBS = {
".github/workflows/tests.yaml": ("simulator_driving", " if: false # FIXME: Started to timeout recently"),
".github/workflows/ui_preview.yaml": ("preview", " if: false # tmp disable due to GH API rate limiting flakiness"),
}
def actionlint() -> Path:
machine = {"x86_64": "amd64", "aarch64": "arm64"}.get(platform.machine(), platform.machine())
target = f"{platform.system().lower()}_{machine}"
if target not in ARCHIVE_SHA256:
raise RuntimeError(f"Unsupported actionlint platform: {target}")
cache = ROOT / ".cache" / f"actionlint-{VERSION}-{target}"
archive = cache / "release.tar.gz"
binary = cache / "actionlint"
cache.mkdir(parents=True, exist_ok=True)
if not archive.exists():
url = f"https://github.com/rhysd/actionlint/releases/download/v{VERSION}/actionlint_{VERSION}_{target}.tar.gz"
with urllib.request.urlopen(url, timeout=60) as response:
data = response.read()
if hashlib.sha256(data).hexdigest() != ARCHIVE_SHA256[target]:
raise RuntimeError("actionlint release checksum mismatch")
with tempfile.NamedTemporaryFile(dir=cache, delete=False) as temporary:
temporary.write(data)
name = temporary.name
os.replace(name, archive)
data = archive.read_bytes()
if hashlib.sha256(data).hexdigest() != ARCHIVE_SHA256[target]:
raise RuntimeError("Cached actionlint release checksum mismatch")
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as release:
member = release.extractfile("actionlint")
if member is None:
raise RuntimeError("actionlint release has no binary")
expected = member.read()
if not binary.exists() or binary.read_bytes() != expected:
with tempfile.NamedTemporaryFile(dir=cache, delete=False) as temporary:
temporary.write(expected)
name = temporary.name
os.chmod(name, 0o755)
os.replace(name, binary)
return binary
def main() -> int:
binary = actionlint()
if sys.argv[1:] == ["--install"]:
return 0
if sys.argv[1:]:
raise RuntimeError("Usage: check_workflows.py [--install]")
workflows = sorted(str(path.relative_to(ROOT)) for path in (ROOT / ".github/workflows").glob("*.yaml"))
workflows += sorted(str(path.relative_to(ROOT)) for path in (ROOT / ".github/workflows").glob("*.yml"))
if not workflows:
raise RuntimeError("No workflows found")
result = subprocess.run([str(binary), "-shellcheck=", "-pyflakes=", "-format", "{{json .}}", *workflows],
cwd=ROOT, text=True, capture_output=True)
if result.returncode not in (0, 1) or result.stderr:
raise RuntimeError(result.stderr or result.stdout or f"actionlint exited {result.returncode}")
errors = json.loads(result.stdout or "[]")
failed = False
for error in errors:
path = error["filepath"]
lines = (ROOT / path).read_text().splitlines()
line = lines[error["line"] - 1]
preceding_jobs = [match.group(1) for text in lines[:error["line"] - 1]
if (match := re.fullmatch(r" ([a-zA-Z_][a-zA-Z0-9_-]*):", text))]
job = preceding_jobs[-1] if preceding_jobs else None
if (error["kind"] == "if-cond" and error["message"] == 'constant expression "false" in condition. remove the if: section'
and path in DISABLED_JOBS and (job, line) == DISABLED_JOBS[path]):
continue
print(f'{path}:{error["line"]}:{error["column"]}: {error["message"]}')
failed = True
return int(failed)
if __name__ == "__main__":
try:
sys.exit(main())
except (OSError, RuntimeError, ValueError) as error:
print(error, file=sys.stderr)
sys.exit(1)