From 7c43eac58d0d7bfa3ab9f755d0ad9aa76b5cb648 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Mon, 2 Nov 2026 22:52:56 +0000 Subject: [PATCH] Keep Minecraft qualification local --- .github/workflows/ci.yml | 26 ++------------- README.md | 10 +++--- docs/RELEASING.md | 14 +++++--- tools/qualification/README.md | 38 +++++++++++++++------- tools/qualification/ci_runtime.py | 11 ++++++- tools/qualification/test_ci_runtime.py | 45 +++++++++++++++++++++++--- 6 files changed, 95 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1477731..9f89f73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: cold-forge-bootstrap: name: Cold Forge bootstrap runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 30 steps: - name: Check out source @@ -81,7 +81,7 @@ jobs: build: name: Build, test, and audit runs-on: ubuntu-latest - timeout-minutes: 240 + timeout-minutes: 30 steps: - name: Check out source @@ -114,7 +114,7 @@ jobs: - name: Build, test, and audit release artifacts run: >- ./gradlew clean check build javadoc verifyReleaseArtifacts writeReleaseChecksums - verifyEclipseProductionClasspath preparePolicyQualification --init-script tools/qualification/probes.init.gradle --no-daemon --stacktrace --max-workers=2 + verifyEclipseProductionClasspath --no-daemon --stacktrace --max-workers=2 -Dorg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" -Dorg.gradle.java.installations.auto-detect=false -Dorg.gradle.java.installations.auto-download=false @@ -136,20 +136,6 @@ jobs: -Dorg.gradle.java.installations.auto-download=false diff -u "$RUNNER_TEMP/first-SHA256SUMS" build/release/SHA256SUMS - - name: Build the isolated packaged-server probe - run: >- - ./gradlew preparePolicyQualification prepareCoalQualification prepareDiamondQualification - prepareGoldQualification prepareMineQualification --init-script tools/qualification/probes.init.gradle - --offline --no-daemon --max-workers=2 - -Dorg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" - -Dorg.gradle.java.installations.auto-detect=false - -Dorg.gradle.java.installations.auto-download=false - - - name: Test packaged policies, imports, reloads and later chunks - env: - GAME_JAVA: ${{ env.JAVA_HOME_8_X64 }} - run: python3 tools/qualification/ci_runtime.py --java "$GAME_JAVA/bin/java" - - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: @@ -173,10 +159,4 @@ jobs: path: | build/test-results/** build/reports/** - build/*-run/logs/** - build/legacy-abi/**/run/logs/** - build/*-integration-run/**/*.properties build/problems/** - build/qualification/**/*.log - build/qualification/**/results.json - build/qualification/**/crash-reports/** diff --git a/README.md b/README.md index fa19391..46470e5 100644 --- a/README.md +++ b/README.md @@ -184,15 +184,17 @@ For a separate test world, use `runMajorIronClient.launch`. It keeps its saves and configuration in `run-major-iron`, apart from the ordinary client launch. The [generation qualification guide](tools/qualification/README.md) describes -the optional packaged-server checks for policies, imports and saved worlds. +the optional local-only packaged-server checks for policies, imports and saved +worlds. The [mine-testing walkthrough](tools/qualification/MINES_TESTING.md) uses a separate Eclipse profile and disposable saves for the new workings. For coalfields, use `prepareCoalMeasuresManual` and the [coal-testing walkthrough](tools/qualification/COAL_TESTING.md). -CI also checks reproducible jars, dependency hashes and ordinary Eclipse -launches. The [release guide](docs/RELEASING.md) explains the manual release -action and its publication checks. +Hosted CI/CD keeps to builds, unit/helper tests, static checks, reproducible +jars, dependency hashes and ordinary Eclipse launch definitions. It does not +run Minecraft servers or gameplay suites. The [release guide](docs/RELEASING.md) +explains the manual release action and its publication checks. Bug reports and feature ideas are welcome through the [issue tracker](https://github.com/MinecraftModDevelopmentMods/RealisticDeposits/issues). diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 9143ee9..202c1a6 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -33,7 +33,13 @@ Do not upload a second file because the first is awaiting moderation. If a job fails after an upload, check its receipt and public destination before retrying. Published versions and tags must never be replaced with new bytes. -Before merging a release, check the README and changelog, repository hygiene, -unit tests, packaged policy/import tests, reloads and later-generated chunks. -Back up important worlds and keep the visual gameplay check separate from -automated test results. +Hosted CI/CD must stay lightweight: builds, unit/helper tests, static checks, +dependency/artifact guards and reproducibility. It must not launch Minecraft +servers, gameplay tests, surveys or policy matrices. + +Before merging a release, check the README and changelog, repository hygiene +and unit tests. Run packaged policy/import tests, reloads and later-generated +chunks locally, retaining results and exact mod/dependency hashes. Reuse that +runtime evidence for workflow/docs-only changes only when all three release +jars and the public dependency are byte-identical. Back up important worlds +and keep the visual gameplay check separate from automated test results. diff --git a/tools/qualification/README.md b/tools/qualification/README.md index 679dd52..77961fd 100644 --- a/tools/qualification/README.md +++ b/tools/qualification/README.md @@ -1,7 +1,9 @@ # Forge 1.10.2 generation qualification -This opt-in check runs the packaged mod on disposable dedicated servers. It -does not open the ordinary development saves or run during a normal build. +This local-only, opt-in check runs the packaged mod on disposable dedicated +servers. It does not open the ordinary development saves or run during a normal +build. Hosted CI/CD runs only builds, unit/helper tests and static/artifact +checks; it must not launch these Minecraft suites. The matrix covers additive, default 25% hybrid, shipped 15% hybrid and replacement policies, each with Manage Vanilla Ores off and on. Each policy @@ -153,10 +155,10 @@ custom channel. Hybrid and replacement limits then missed that ordinary ore. The regression checks require 4.1.3 to reduce imported ordinary generation while preserving explicit custom channels, saved settings and old terrain. -CI prepares an official Forge server from a checksum-pinned installer and -runs the complete 32-case fresh/reload/later-chunk matrix. Failures retain -their logs and result reports as workflow artifacts. The optional local -upgrade suite also checks disposable copies of worlds made before the fix. +Local qualification uses an official Forge server from a checksum-pinned +installer and runs the complete 32-case fresh/reload/later-chunk matrix. +Retain failed logs, result reports and disposable worlds locally. The upgrade +suite also checks disposable copies of worlds made before the fix. ## Diamond-pipe workings @@ -173,22 +175,34 @@ target's compatible jar through `--mineralogy`. `mineralogy-flat` checks partial workings with active strata and no cross-chunk vanilla decoration. Native terrain cases populate the complete mine footprint before checking reloads; vanilla decoration in adjacent new chunks can otherwise change unworked ground. -CI runs the controlled cases. +The combined local pass runs the controlled cases. ## Gold and the combined regression pass -The packaged CI driver covers iron, coal, diamonds and gold, plus a separate +The combined local driver covers iron, coal, diamonds and gold, plus a separate Mesa gold policy matrix. It also runs source-disable, gravel-harvest and workings checks. Each policy matrix includes imports, explicit overrides, saved management toggles, reloads and later-generated chunks. Logged errors fail the driver even when a probe has written a pass marker. -`ci_runtime.py --only ` selects a smaller local pass. Its optional +The historical filename `ci_runtime.py` is retained for existing local commands; +the driver refuses execution when `CI` or `GITHUB_ACTIONS` is true. Prepare its +isolated probes locally first: + +```powershell +.\gradlew.bat preparePolicyQualification prepareCoalQualification prepareDiamondQualification prepareGoldQualification prepareMineQualification --init-script tools/qualification/probes.init.gradle +``` + +Then run `python tools/qualification/ci_runtime.py --java ` +with a fresh evidence directory through `--output`. Supply `--only ` +for a smaller local pass. Its optional `--runtime`, `--mod`, `--orespawn` and `--probe-directory` arguments reuse sealed inputs; the engine must still match the public pin. They do not enable a local -engine override or change the supplied runtime. Without those options, CI -installs the checksum-pinned official Forge server and downloads the public -dependency itself. +engine override or change the supplied runtime. Without those options, the +local driver installs the checksum-pinned official Forge server and downloads +the public dependency itself. Keep the candidate and dependency hashes with +the local results. A workflow/docs-only change does not invalidate exact-jar +runtime evidence when all three release jars and dependency remain identical. `gold_survey_110.py` measures six fixed seeds, reserving the last two as holdouts. Run lode, `--control` and `--native-sediment` passes, then combine diff --git a/tools/qualification/ci_runtime.py b/tools/qualification/ci_runtime.py index a21ec1a..a2c3280 100644 --- a/tools/qualification/ci_runtime.py +++ b/tools/qualification/ci_runtime.py @@ -1,8 +1,9 @@ -"""Prepare an official Forge server and run the packaged policy matrix.""" +"""Run local-only packaged qualification with an official Forge server.""" import argparse import hashlib import json +import os import re import shutil import subprocess @@ -15,6 +16,13 @@ FORGE_HASH = "809C2EA5B4D4F3E5A294D3A326744F20FEC412A1263E708297D00F512E377542" +def require_local_execution(environment): + if any(environment.get(name, "").lower() in ("true", "1") + for name in ("CI", "GITHUB_ACTIONS")): + raise RuntimeError("Packaged Minecraft qualification is local-only; " + "hosted CI/CD must use lightweight checks") + + def download(url, target, expected): target.parent.mkdir(parents=True, exist_ok=True) subprocess.run(["curl", "--fail", "--location", "--retry", "3", @@ -73,6 +81,7 @@ def check_runtime_log(text): def main(): + require_local_execution(os.environ) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--java", required=True, type=Path) parser.add_argument("--output", type=Path, default=ROOT / "build/qualification/packaged") diff --git a/tools/qualification/test_ci_runtime.py b/tools/qualification/test_ci_runtime.py index ddf61d9..16d5dc8 100644 --- a/tools/qualification/test_ci_runtime.py +++ b/tools/qualification/test_ci_runtime.py @@ -1,12 +1,14 @@ -"""Check which packaged suites the CI driver actually runs.""" +"""Keep hosted checks lightweight and retain complete local runtime coverage.""" +import re import unittest from pathlib import Path +from unittest.mock import patch import ci_runtime -class PackagedCiPlanTest(unittest.TestCase): +class LocalPackagedPlanTest(unittest.TestCase): def test_a_pass_marker_cannot_hide_a_runtime_error(self): ci_runtime.check_runtime_log("[main/ERROR]: FML appears to be missing any signature data\nPASS") for line in ("[Server thread/FATAL]: Error executing task", "[main/ERROR]: Missing a model", @@ -14,11 +16,44 @@ def test_a_pass_marker_cannot_hide_a_runtime_error(self): with self.assertRaises(AssertionError): ci_runtime.check_runtime_log(line + "\nRD_GOLD_PASS first") - def test_the_workflow_prepares_every_probe_used_by_the_driver(self): - workflow = (ci_runtime.ROOT / ".github/workflows/ci.yml").read_text() + def test_local_init_script_prepares_every_probe_used_by_the_driver(self): + script = (ci_runtime.ROOT / "tools/qualification/probes.init.gradle").read_text() for task in ("preparePolicyQualification", "prepareCoalQualification", "prepareDiamondQualification", "prepareGoldQualification", "prepareMineQualification"): - self.assertIn(task, workflow) + self.assertIn(task, script) + + def test_hosted_workflows_never_launch_packaged_minecraft_suites(self): + for path in (ci_runtime.ROOT / ".github/workflows").glob("*.yml"): + workflow = path.read_text() + self.assertNotIn("ci_runtime.py", workflow, str(path)) + self.assertNotIn("probes.init.gradle", workflow, str(path)) + self.assertNotRegex(workflow, r"\bprepare\w*Qualification\b", str(path)) + self.assertNotRegex(workflow, r"\brun(?:Server|Client|TestServer)\b", str(path)) + self.assertNotRegex(workflow, r"\b\w+_110\.py\b", str(path)) + for suite in ci_runtime.suite_plan().values(): + self.assertNotIn(suite["script"], workflow, str(path)) + + def test_ci_build_has_a_short_timeout(self): + workflow = (ci_runtime.ROOT / ".github/workflows/ci.yml").read_text() + build = workflow.split(" build:\n", 1)[1] + timeout = re.search(r"timeout-minutes:\s*(\d+)", build) + self.assertIsNotNone(timeout) + self.assertLessEqual(int(timeout.group(1)), 30) + + def test_packaged_driver_rejects_hosted_execution(self): + for environment in ({"GITHUB_ACTIONS": "true"}, {"CI": "true"}, {"CI": "1"}): + with self.subTest(environment=environment): + with self.assertRaisesRegex(RuntimeError, "local-only"): + ci_runtime.require_local_execution(environment) + + def test_packaged_driver_accepts_local_execution(self): + ci_runtime.require_local_execution({}) + ci_runtime.require_local_execution({"CI": "false"}) + + def test_main_rejects_ci_before_preparing_any_inputs(self): + with patch.dict("os.environ", {"GITHUB_ACTIONS": "true"}): + with self.assertRaisesRegex(RuntimeError, "local-only"): + ci_runtime.main() def test_every_deposit_has_policy_and_gameplay_coverage(self): plan = ci_runtime.suite_plan()