Skip to content
Merged
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
26 changes: 3 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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/**
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
14 changes: 10 additions & 4 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
38 changes: 26 additions & 12 deletions tools/qualification/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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 <suite names>` 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 <Java-8-executable>`
with a fresh evidence directory through `--output`. Supply `--only <suite names>`
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
Expand Down
11 changes: 10 additions & 1 deletion tools/qualification/ci_runtime.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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",
Expand Down Expand Up @@ -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")
Expand Down
45 changes: 40 additions & 5 deletions tools/qualification/test_ci_runtime.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,59 @@
"""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",
"Tried to assign a mutable BlockPos", "GL ERROR 1281"):
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()
Expand Down
Loading