Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/kernel-ci-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ jobs:
path: out
run-id: ${{ env.REUSE_RUN }}
github-token: ${{ github.token }}
# inspect-package.py resolves module symbols with depmod and unpacks
# with tar --zstd; the Namespace builder image ships neither.
- name: Ensure kmod and zstd for the package checks
run: |
if ! command -v depmod >/dev/null || ! command -v zstd >/dev/null; then
sudo apt-get update -q
sudo apt-get install -y -q --no-install-recommends kmod zstd
fi
- name: Inspect the packages
run: |
python3 -I linux/tools/aurora-ci/inspect-package.py --packages out/packages \
Expand Down
37 changes: 22 additions & 15 deletions tools/aurora-ci/lookup-build.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@

This script computes the key, pins the omarchy-pkgs commit it was computed
from, and looks those artifacts up through the Actions API. It accepts only
artifacts that this repository's kernel CI uploaded from its own branches,
never one from a fork's pull request (see TRUSTED_WORKFLOWS).
artifacts this repository's kernel CI uploaded (see TRUSTED_WORKFLOWS);
a reused package or verification record must also come from a non-pull-request
run (REUSE_EVENTS), while a comparison baseline may come from a pull request.
"""

import argparse
Expand Down Expand Up @@ -84,7 +85,14 @@ def build_key(kernel_tree, recipe_trees):
# from runs of this repository's kernel CI on this repository's own branches
# and events. Pushing a branch here needs write access; a fork does not.
TRUSTED_WORKFLOWS = {".github/workflows/kernel-ci.yml", ".github/workflows/kernel-ci-label.yml"}
TRUSTED_EVENTS = {"push", "merge_group", "schedule", "workflow_dispatch", "pull_request"}
# A package or a verification record is substituted into a later run instead
# of rebuilding/retesting, so it may only be reused from a run that is not a
# pull request: a writer's CI-only pull request has the same build key as the
# branch tip and could otherwise plant a package before anyone reviews it.
REUSE_EVENTS = {"push", "merge_group", "schedule", "workflow_dispatch"}
# A comparison baseline is only read, never substituted, so a pull request's
# metadata is fine as a baseline.
BASELINE_EVENTS = REUSE_EVENTS | {"pull_request"}


class Api:
Expand All @@ -105,19 +113,18 @@ def get(self, path, **query):
with urllib.request.urlopen(request, timeout=60) as response:
return json.load(response)

def trusted_run(self, run_id):
"""Whether a run is this repository's kernel CI on one of its own
branches (or the queue, a schedule or a manual run), not a fork's."""
def trusted_run(self, run_id, events):
"""Whether a run is this repository's kernel CI, from one of the given
events, on this repository (not a fork)."""
if run_id not in self._runs:
run = self.get(f"actions/runs/{run_id}")
self._runs[run_id] = (
run.get("event") in TRUSTED_EVENTS
self._runs[run_id] = self.get(f"actions/runs/{run_id}")
run = self._runs[run_id]
return (run.get("event") in events
and run.get("path") in TRUSTED_WORKFLOWS
and (run.get("repository") or {}).get("id") == self.repository_id
and (run.get("head_repository") or {}).get("id") == self.repository_id)
return self._runs[run_id]

def newest_artifact(self, name):
def newest_artifact(self, name, events):
"""Run ID of the newest unexpired artifact with this exact name that a
trusted run uploaded, or ''."""
data = self.get("actions/artifacts", name=name, per_page=100)
Expand All @@ -127,7 +134,7 @@ def newest_artifact(self, name):
and a.get("workflow_run", {}).get("head_repository_id") == self.repository_id]
live.sort(key=lambda a: a.get("created_at", ""), reverse=True)
for artifact in live:
if self.trusted_run(artifact["workflow_run"]["id"]):
if self.trusted_run(artifact["workflow_run"]["id"], events):
return str(artifact["workflow_run"]["id"])
return ""

Expand All @@ -140,12 +147,12 @@ def lookup(api, kernel, recipe_repo, base_sha, force):
out = {"key": key, "kernel_tree": kernel_tree, "recipe_commit": recipe_commit,
"package_run": "", "verified_run": "", "base_tree": "", "baseline_run": ""}
if not force:
out["package_run"] = api.newest_artifact(f"kernel-package-{key}")
out["verified_run"] = api.newest_artifact(f"kernel-verified-{key}")
out["package_run"] = api.newest_artifact(f"kernel-package-{key}", REUSE_EVENTS)
out["verified_run"] = api.newest_artifact(f"kernel-verified-{key}", REUSE_EVENTS)
if base_sha:
ensure_commit(kernel, base_sha)
out["base_tree"] = kernel_id(kernel, base_sha)
out["baseline_run"] = api.newest_artifact(f"kernel-metadata-{out['base_tree']}")
out["baseline_run"] = api.newest_artifact(f"kernel-metadata-{out['base_tree']}", BASELINE_EVENTS)
return out


Expand Down
35 changes: 21 additions & 14 deletions tools/aurora-ci/tests/test_lookup_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ class FakeApi:
def __init__(self, artifacts=None):
self.artifacts, self.asked = artifacts or {}, []

def newest_artifact(self, name):
self.asked.append(name)
def newest_artifact(self, name, events):
self.asked.append((name, frozenset(events)))
return self.artifacts.get(name, "")


Expand Down Expand Up @@ -101,18 +101,16 @@ def test_newest_unexpired_artifact_from_a_trusted_run(self):
api = self.api([artifact(1, "2026-10-20", expired=True), artifact(2, "2026-10-18"),
artifact(3, "2026-10-19"), artifact(4, "2026-10-21", name="other")],
{2: run(), 3: run("merge_group")})
self.assertEqual(api.newest_artifact("kernel-package-x"), "3")
self.assertEqual(self.api([], {}).newest_artifact("kernel-package-x"), "")
self.assertEqual(api.newest_artifact("kernel-package-x", lookup.REUSE_EVENTS), "3")
self.assertEqual(self.api([], {}).newest_artifact("kernel-package-x", lookup.REUSE_EVENTS), "")

def test_a_fork_run_is_never_reused(self):
# A fork's pull request uploads a newer artifact under the same name.
api = self.api([artifact(2, "2026-10-18"), artifact(9, "2026-10-20", head_repo=2000)],
{2: run(), 9: run("pull_request", head_repo=2000)})
self.assertEqual(api.newest_artifact("kernel-package-x"), "2")
self.assertEqual(api.newest_artifact("kernel-package-x", lookup.BASELINE_EVENTS), "2")
self.assertNotIn("actions/runs/9", api.calls)

def test_the_run_itself_must_be_trusted(self):
# The artifact claims this repository, but the run says otherwise.
cases = {
"fork head": run("pull_request", head_repo=2000),
"another workflow": run(path=".github/workflows/something-else.yml"),
Expand All @@ -121,12 +119,21 @@ def test_the_run_itself_must_be_trusted(self):
for name, untrusted in cases.items():
with self.subTest(name):
api = self.api([artifact(5, "2026-10-20")], {5: untrusted})
self.assertEqual(api.newest_artifact("kernel-package-x"), "")

def test_branches_of_this_repository_and_every_trusted_event(self):
for event in sorted(lookup.TRUSTED_EVENTS):
self.assertEqual(api.newest_artifact("kernel-package-x", lookup.BASELINE_EVENTS), "")

def test_package_and_verified_reuse_excludes_pull_requests(self):
# A writer's CI-only PR run uploads a package under the branch tip's key.
api = self.api([artifact(7, "2026-10-20")], {7: run("pull_request")})
self.assertEqual(api.newest_artifact("kernel-package-x", lookup.REUSE_EVENTS), "",
"a pull-request run must not be reused as a package")
# The same PR run, as a comparison baseline, is acceptable.
api = self.api([artifact(7, "2026-10-20", name="kernel-metadata-x")], {7: run("pull_request")})
self.assertEqual(api.newest_artifact("kernel-metadata-x", lookup.BASELINE_EVENTS), "7")

def test_reuse_events_cover_the_non_pr_triggers(self):
for event in sorted(lookup.REUSE_EVENTS):
with self.subTest(event):
api = self.api([artifact(5, "2026-10-20")], {5: run(event)})
self.assertEqual(api.newest_artifact("kernel-package-x"), "5")
api = self.api([artifact(6, "2026-10-20")], {6: run("pull_request", ".github/workflows/kernel-ci-label.yml")})
self.assertEqual(api.newest_artifact("kernel-package-x"), "6")
self.assertEqual(api.newest_artifact("kernel-package-x", lookup.REUSE_EVENTS), "5")
self.assertNotIn("pull_request", lookup.REUSE_EVENTS)
self.assertIn("pull_request", lookup.BASELINE_EVENTS)
Loading