Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8e389bf
feat: add DR-008 resolved-dependency resolve + override mechanism
Subramanian-K812 Jul 1, 2026
5a04b40
fix: remove bazel_config, always overwrite overrides, clean injection…
Subramanian-K812 Jul 2, 2026
b7e7f69
fix: revert update_module_from_known_good.py, move generate_override_…
Subramanian-K812 Jul 2, 2026
c20460d
fix: ruff format fix for scripts/tooling/BUILD
Subramanian-K812 Jul 2, 2026
a388b1f
fix: revert out-of-scope changes to module.py, update_module_from_kno…
Subramanian-K812 Jul 2, 2026
376cd1b
feat: warn on unresolved declared deps, add bazel test target for kno…
Subramanian-K812 Jul 3, 2026
f72a9ef
fix: overwrite() replaces a module's own override instead of duplicat…
Subramanian-K812 Jul 24, 2026
f1afd65
feat: reach one level of transitive git_override deps via a module's …
Subramanian-K812 Aug 3, 2026
33df9a6
feat: pin a module's full transitive closure from the Stage-1 graph i…
Subramanian-K812 Aug 6, 2026
8407ad8
fix: key the Stage-2 pin scope on ref_int's resolved set
Subramanian-K812 Aug 11, 2026
e336ba0
feat: lock the versions of deps Stage 2 collects test artifacts from
Subramanian-K812 Aug 11, 2026
5bce4be
fix: pin rules_oci by commit and report every pin's provenance, confl…
Subramanian-K812 Aug 12, 2026
d72eaa7
fix: correct stale claims in comments
Subramanian-K812 Aug 12, 2026
4401c58
fix: match Bazel's version ordering exactly and surface non-verbose g…
Subramanian-K812 Aug 14, 2026
b3facb9
fix: resolve resolve_deps CLI paths
Subramanian-K812 Aug 21, 2026
5b4774b
feat: apply module-under-test's own bazel_patches in Stage 2, add rc …
Subramanian-K812 Aug 24, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/internal_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
internal_tests:
uses: eclipse-score/cicd-workflows/.github/workflows/tests.yml@main
with:
bazel-target: "test //scripts/tooling:tooling_tests"
bazel-target: "test //scripts/tooling:tooling_tests //scripts/known_good:known_good_tests"
5 changes: 5 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ include("//bazel_common:score_modules_target_sw.MODULE.bazel")
# Score test images
include("//bazel_common:score_images.MODULE.bazel")

# Single-version locks for the deps Stage 2 collects test artifacts from (GTest, the Rust
# test rules, the ferrocene coverage tooling). Read after the includes above so it pins the
# versions they bring in transitively.
include("//bazel_common:score_test_artifact_versions.MODULE.bazel")

bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost")
archive_override(
module_name = "rules_boost",
Expand Down
21 changes: 0 additions & 21 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion bazel_common/score_images.MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
# Spelled as a commit, not `tag = "v2.3.1"`: the Stage-1 manifest carries immutable commits only, so
# a tag-pinned git_override was dropped from it silently and every module resolved its own rules_oci.
bazel_dep(name = "rules_oci", version = "2.3.1")
git_override(
module_name = "rules_oci",
commit = "f214185dcf149090cb3212e878f692eb2c8c0d3d", # v2.3.1
remote = "https://github.com/bazel-contrib/rules_oci.git",
tag = "v2.3.1",
)

oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")
Expand Down
41 changes: 41 additions & 0 deletions bazel_common/score_test_artifact_versions.MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
#
# Deliberate ceilings for the deps Stage 2 collects test artifacts from.
#
# ref_int's resolved set is imposed on every module under test whether a dependency is named here or
# not; this file does not decide *which* deps get pinned. It decides which of those pins are a
# decision rather than an inheritance. `bazel_dep(version = ...)` is only a floor that MVS raises
# silently, while `single_version_override` is also a ceiling, so the versions below are ones Stage 1
# reports as `asserted` rather than `incidental`.
#
# Every version here equals what MVS resolves today, so this changes no build now -- only what
# happens the day something in the graph asks for more.

# C++ test binaries and the coverage .dat files genhtml reads.
single_version_override(
module_name = "googletest",
version = "1.17.0.bcr.2",
)

# Rust test rules that build the .profraw-emitting binaries.
single_version_override(
module_name = "rules_rust",
version = "0.68.2-score",
)

# Ferrocene coverage tooling behind those .profraw files.
single_version_override(
module_name = "score_toolchains_rust",
version = "0.8.0",
)
63 changes: 63 additions & 0 deletions scripts/known_good/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
load("@rules_python//python:defs.bzl", "py_binary", "py_library")
load("@score_tooling//python_basics:defs.bzl", "score_py_pytest")

# Library target: the known_good package (models + generators).
# Depended on by the test and binary targets below. Note //scripts/tooling has its own separate
# lib/known_good package and does not use this one.
py_library(
name = "known_good",
srcs = glob(
["**/*.py"],
exclude = ["tests/**"],
),
visibility = ["//visibility:public"],
)

# Tests for the known_good package (currently: ResolvedDependencies).
# Not part of //scripts/tooling:tooling_tests, whose glob is scoped to scripts/tooling/tests/.
score_py_pytest(
name = "known_good_tests",
srcs = glob(["tests/**/*.py"]),
data = ["//:known_good.json"],
pytest_config = "//:pyproject.toml",
deps = [":known_good"],
)

# Runnable binary for the resolve + inject workflow.
#
# Stage 1 (export) — 'bazel mod graph' is a prerequisite; run it first and pass the result:
# bazel mod graph --verbose --output=json > graph.json
# bazel run //scripts/known_good:resolve_deps -- \
# --mod-graph graph.json --export _resolved_deps/resolved_versions.json
# Writes the manifest, graph.json and resolved_pins_report.json side by side; all three are
# published as the stage1-resolved-deps artifact. Paths are resolved against
# BUILD_WORKSPACE_DIRECTORY, so graph.json does not need to be listed in data = [...].
#
# '--verbose' adds 'originalVersion' to each edge, the only record of a consumer asking for a
# version other than the one ref_int imposes. It is a strict superset, so Stage 2 reads the same
# graph.json either way; without it the export still succeeds, with every verdict 'unknown'.
#
# Stage 2 (inject) — consumes that same directory:
# bazel run //scripts/known_good:resolve_deps -- \
# _module/MODULE.bazel --resolved-deps _resolved_deps/
# The manifest supplies each module's resolved version; graph.json identifies the
# module-under-test's transitive closure so all of it is pinned, not only direct deps.
py_binary(
name = "resolve_deps",
srcs = ["resolved_dependencies.py"],
main = "resolved_dependencies.py",
visibility = ["//visibility:public"],
deps = [":known_good"],
)
109 changes: 109 additions & 0 deletions scripts/known_good/module_patches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
"""Apply the module-under-test's own ``bazel_patches`` to its Stage-2 checkout.

Distinct from the ``bazel_patches`` strip in ``ResolvedDependencies.overwrite``: that drops
``//patches/...`` labels from *injected dependency overrides* (they do not resolve inside
another module's root); this applies the module's own patches by filesystem path, emitting
no label. Both are correct at once.
"""

from __future__ import annotations

import re
import subprocess
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .models.module import Module

# ``//patches/<dir>:<file>``; known_good.json also carries bare paths, so both are accepted.
_LABEL_RE = re.compile(r"^//(?P<pkg>[^:]+):(?P<name>.+)$")

# Matches the patch_strip emitted with these same patches in Stage-1 override directives.
PATCH_STRIP = 1


class ModulePatchError(RuntimeError):
"""A declared patch could not be resolved or applied.

Always a ref_int defect: ref_int's recorded patch no longer matches the commit it is
declared against.
"""


def resolve_patch_path(patch: str, ref_int_root: Path) -> Path:
"""Map one ``bazel_patches`` entry (label or bare path) to a file in ref_int's tree."""
match = _LABEL_RE.match(patch)
relative = Path(match.group("pkg")) / match.group("name") if match else Path(patch)
if relative.is_absolute():
raise ModulePatchError(f"patch entry must be workspace-relative, got absolute path: {patch!r}")
resolved = (ref_int_root / relative).resolve()
if not resolved.is_file():
raise ModulePatchError(f"declared patch {patch!r} does not exist at {resolved}")
return resolved


def _git_apply(workspace: Path, patch_file: Path, *extra: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
["git", "-C", str(workspace), "apply", f"-p{PATCH_STRIP}", *extra, str(patch_file)],
capture_output=True,
text=True,
check=False,
)


def apply_module_patches(
module: Module,
workspace: Path,
ref_int_root: Path,
log=print,
) -> list[str]:
"""Apply ``module``'s declared patches to ``workspace`` in declaration order.

Returns the newly applied entries; already-applied ones are reported and skipped. Raises
:class:`ModulePatchError` on the first entry that neither applies nor is already applied —
silently testing an unpatched checkout is the bug this step exists to fix.
"""
if not module.bazel_patches:
log(f"QR: {module.name} declares no bazel_patches; checkout is used as-is")
return []

applied: list[str] = []
for patch in module.bazel_patches:
patch_file = resolve_patch_path(patch, ref_int_root)

# Idempotency for local re-runs (ci_local.sh reuses _module/); fresh CI never hits it.
if _git_apply(workspace, patch_file, "--reverse", "--check").returncode == 0:
log(f"QR: {patch} already applied to {workspace}; skipping")
continue

dry_run = _git_apply(workspace, patch_file, "--check")
if dry_run.returncode != 0:
raise ModulePatchError(
f"ref_int harness defect: patch {patch} does not apply to {module.name} "
f"at commit {module.hash}, and is not already applied.\n"
f"git apply --check said:\n{dry_run.stderr.strip()}"
)

result = _git_apply(workspace, patch_file)
if result.returncode != 0:
raise ModulePatchError(
f"ref_int harness defect: patch {patch} passed --check but failed to apply to "
f"{module.name}.\ngit apply said:\n{result.stderr.strip()}"
)
log(f"QR: applied {patch} to {workspace}")
applied.append(patch)

return applied
Loading
Loading