mirror of
https://github.com/rustfs/rustfs.git
synced 2026-10-13 08:02:03 +00:00
ci: reuse workspace test builds for serial ILM checks (#8465)
This commit is contained in:
@@ -684,12 +684,12 @@ def check_ilm_build_budget(root: Path) -> list[str]:
|
||||
return [".github/workflows/ci.yml: missing workflow"]
|
||||
workflow = workflow_path.read_text()
|
||||
job = re.search(
|
||||
r"^ test-ilm-integration-serial:\n(?P<body>.*?)(?=^ [a-z0-9-]+:\n)",
|
||||
r"^ test-and-lint:\n(?P<body>.*?)(?=^ [a-z0-9-]+:\n)",
|
||||
workflow,
|
||||
re.MULTILINE | re.DOTALL,
|
||||
)
|
||||
if job is None:
|
||||
return [".github/workflows/ci.yml: missing test-ilm-integration-serial job"]
|
||||
return [".github/workflows/ci.yml: ILM integration must share the workspace test job"]
|
||||
|
||||
step = re.search(
|
||||
r"^ - name: Run ignored ILM integration tests serially\n(?P<body>.*?)(?=^ - name:|\Z)",
|
||||
@@ -705,6 +705,16 @@ def check_ilm_build_budget(root: Path) -> list[str]:
|
||||
)
|
||||
if budget not in step.group("body"):
|
||||
return [".github/workflows/ci.yml: ILM integration must keep the measured 3/2 Cargo build budget"]
|
||||
for required in (
|
||||
" if: needs.classify-changes.outputs.mode == 'full'",
|
||||
"cargo nextest run --profile default -j1 --run-ignored ignored-only",
|
||||
"--all --exclude e2e_test",
|
||||
"-E 'binary(lifecycle_integration_test) or (package(rustfs) and test(lifecycle_transition_api_test))'",
|
||||
'status=${PIPESTATUS[0]}',
|
||||
'exit "${status}"',
|
||||
):
|
||||
if required not in step.group("body"):
|
||||
return [f".github/workflows/ci.yml: ILM integration is missing {required.strip()}"]
|
||||
return []
|
||||
|
||||
|
||||
@@ -5566,12 +5576,17 @@ class SelfTests(unittest.TestCase):
|
||||
workflow.parent.mkdir(parents=True)
|
||||
valid = (
|
||||
"jobs:\n"
|
||||
" test-ilm-integration-serial:\n"
|
||||
" test-and-lint:\n"
|
||||
" steps:\n"
|
||||
" - name: Run ignored ILM integration tests serially\n"
|
||||
" if: needs.classify-changes.outputs.mode == 'full'\n"
|
||||
" env:\n"
|
||||
" CARGO_BUILD_JOBS: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && '3' || '2' }}\n"
|
||||
" run: cargo nextest run\n"
|
||||
" run: |\n"
|
||||
" cargo nextest run --profile default -j1 --run-ignored ignored-only --all --exclude e2e_test \\\n"
|
||||
" -E 'binary(lifecycle_integration_test) or (package(rustfs) and test(lifecycle_transition_api_test))'\n"
|
||||
" status=${PIPESTATUS[0]}\n"
|
||||
' exit "${status}"\n'
|
||||
" next-job:\n"
|
||||
" steps: []\n"
|
||||
)
|
||||
@@ -5580,6 +5595,18 @@ class SelfTests(unittest.TestCase):
|
||||
|
||||
workflow.write_text(valid.replace(" CARGO_BUILD_JOBS:", " BUILD_JOBS:"))
|
||||
self.assertEqual(len(check_ilm_build_budget(root)), 1)
|
||||
for old, new in (
|
||||
("test-and-lint:", "test-ilm-integration-serial:"),
|
||||
("outputs.mode == 'full'", "outputs.mode == 'docs'"),
|
||||
("--profile default", "--profile ci"),
|
||||
("-j1", "-j2"),
|
||||
("ignored-only", "default"),
|
||||
("--all --exclude e2e_test", "-p rustfs-scanner -p rustfs"),
|
||||
("binary(lifecycle_integration_test)", "none()"),
|
||||
('exit "${status}"', "exit 0"),
|
||||
):
|
||||
workflow.write_text(valid.replace(old, new))
|
||||
self.assertTrue(check_ilm_build_budget(root), (old, new))
|
||||
|
||||
def test_vault_tests_require_cross_process_serialization(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
||||
+41
-1
@@ -16,7 +16,7 @@ import unittest
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
ALWAYS_JOBS = ("classify-changes", "typos", "quick-checks")
|
||||
CODE_JOBS = (
|
||||
"test-and-lint", "offline-enrollment-e2e", "test-ilm-integration-serial", "test-and-lint-rio-v2",
|
||||
"test-and-lint", "offline-enrollment-e2e", "test-and-lint-rio-v2",
|
||||
"connect-short-credential-boundary", "test-and-lint-protocols",
|
||||
"build-rustfs-debug-binary", "uring-integration", "e2e-tests",
|
||||
"s3-implemented-tests", "s3-lifecycle-behavior-tests",
|
||||
@@ -224,6 +224,10 @@ def check_workflow(root: Path) -> list[str]:
|
||||
block = yaml_block(jobs, job, 2) or []
|
||||
if scalar(block, "if", 4) != "needs.classify-changes.outputs.mode == 'full' && github.event_name != 'pull_request'":
|
||||
errors.append(f"{job} must retain full validation on every non-PR event")
|
||||
workspace = "\n".join(yaml_block(jobs, "test-and-lint", 2) or [])
|
||||
clippy = re.search(r"^ - name: Run clippy lints\n(?P<body>.*?)(?=^ - name:|\Z)", workspace, re.MULTILINE | re.DOTALL)
|
||||
if clippy is None or scalar(clippy["body"].splitlines(), "if", 8) != "github.event_name != 'pull_request'":
|
||||
errors.append("workspace Clippy must run on every non-PR event")
|
||||
if scalar(gate, "shell", 8) != "bash" or scalar(gate, "run", 8) != "python3 scripts/ci_gate.py verify" or scalar(gate, "CI_NEEDS", 10) != "${{ toJSON(needs) }}":
|
||||
errors.append("required-checks must verify the actual needs results")
|
||||
if any(re.match(r'''\s+(?:- )?(?:["']?continue-on-error["']?\s*:|["']?if["']?\s*:)''', line) and not line.startswith(" if:") for line in gate):
|
||||
@@ -387,6 +391,9 @@ class SelfTests(unittest.TestCase):
|
||||
(root / ".github/workflows").mkdir(parents=True)
|
||||
source = (ROOT / ".github/workflows/ci.yml").read_text()
|
||||
path = root / ".github/workflows/ci.yml"
|
||||
for condition in ("false", "github.event_name == 'schedule'", "github.event_name == 'pull_request'"):
|
||||
path.write_text(source.replace(" - name: Run clippy lints\n if: github.event_name != 'pull_request'", f" - name: Run clippy lints\n if: {condition}"))
|
||||
self.assertIn("workspace Clippy must run on every non-PR event", check_workflow(root))
|
||||
for job in EXTENDED_FEATURE_JOBS:
|
||||
before, block = source.split(f" {job}:\n", 1)
|
||||
for condition in ("false", "github.event_name == 'schedule'", "github.event_name != 'pull_request'"):
|
||||
@@ -462,6 +469,39 @@ class SelfTests(unittest.TestCase):
|
||||
self.assertEqual(result.returncode, code, result.stderr)
|
||||
self.assertIn("ERROR:" if code else "CI contract passed", result.stderr if code else result.stdout)
|
||||
|
||||
def test_merged_ilm_step_preserves_exit_status_and_core_report(self):
|
||||
from check_test_wiring import yaml_block
|
||||
from test_security_workflow import named_steps, shell_body
|
||||
|
||||
workflow = (ROOT / ".github/workflows/ci.yml").read_text().splitlines()
|
||||
step = named_steps(yaml_block(workflow, "test-and-lint", 2))["Run ignored ILM integration tests serially"]
|
||||
body = shell_body(step)
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
commands = root / "bin"
|
||||
commands.mkdir()
|
||||
# Exercise the actual pipeline without building the workspace.
|
||||
fake_timeout = commands / "timeout"
|
||||
fake_timeout.write_text('#!/bin/sh\nshift 4\nexec "$@"\n')
|
||||
fake_timeout.chmod(0o755)
|
||||
cargo = commands / "cargo"
|
||||
cargo.write_text('#!/bin/sh\nprintf "%s\\n" "$@" > "$ILM_ARGS"\nexit "$ILM_STATUS"\n')
|
||||
cargo.chmod(0o755)
|
||||
report = root / "target/nextest/ci/junit.xml"
|
||||
report.parent.mkdir(parents=True)
|
||||
report.write_text("existing workspace report")
|
||||
args = root / "args.txt"
|
||||
for status in (0, 23, 124):
|
||||
env = dict(os.environ, PATH=f"{commands}{os.pathsep}{os.environ['PATH']}",
|
||||
ILM_ARGS=str(args), ILM_STATUS=str(status))
|
||||
result = subprocess.run(["bash", "-e", "-o", "pipefail", "-c", body], cwd=root, env=env, capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, status, result.stderr)
|
||||
self.assertEqual(report.read_text(), "existing workspace report")
|
||||
arguments = args.read_text().splitlines()
|
||||
self.assertEqual(arguments[:9], ["nextest", "run", "--profile", "default", "-j1", "--run-ignored", "ignored-only", "--all", "--exclude"])
|
||||
self.assertEqual(arguments[9], "e2e_test")
|
||||
self.assertIn(f"exit_status={status}", (root / "artifacts/ilm-integration/diagnostics.txt").read_text())
|
||||
|
||||
def test_actual_selector_bootstrap_uses_base_policy_and_fails_closed(self):
|
||||
from check_test_wiring import yaml_block
|
||||
jobs = yaml_block((ROOT / ".github/workflows/ci.yml").read_text().splitlines(), "jobs", 0)
|
||||
|
||||
Reference in New Issue
Block a user