Skip to content

[Chore]: 准备 Qz-Miner 5.1.1 发布 #24

[Chore]: 准备 Qz-Miner 5.1.1 发布

[Chore]: 准备 Qz-Miner 5.1.1 发布 #24

Workflow file for this run

name: Branch build
on:
push:
branches: [ '**' ]
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
prepare-baselines:
name: Prepare GTNH baseline matrix
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.baselines.outputs.matrix }}
steps:
- name: Checkout mod repo
uses: actions/checkout@v5
with:
fetch-depth: 32
submodules: recursive
- name: Validate baseline manifest and build matrix
id: baselines
shell: bash
run: |
python3 <<'PY'
import json
import os
import re
from pathlib import Path
SAFE_VALUE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
def fail(message):
raise SystemExit(message)
def reject_duplicates(pairs):
result = {}
for key, value in pairs:
if key in result:
fail(f"duplicate JSON key: {key}")
result[key] = value
return result
def reject_constant(value):
fail(f"invalid JSON constant: {value}")
manifest_path = Path("gradle/gtnh-baselines.json")
raw = manifest_path.read_bytes()
if len(raw) > 16384:
fail("baseline manifest exceeds 16 KiB")
try:
text = raw.decode("ascii")
except UnicodeDecodeError:
fail("baseline manifest must contain ASCII only")
data = json.loads(
text,
object_pairs_hook=reject_duplicates,
parse_constant=reject_constant,
)
if type(data) is not dict or set(data) != {"schemaVersion", "baselines"}:
fail("baseline manifest must contain only schemaVersion and baselines")
if type(data["schemaVersion"]) is not int or data["schemaVersion"] != 1:
fail("unsupported baseline manifest schemaVersion")
baselines = data["baselines"]
if type(baselines) is not list or len(baselines) != 2:
fail("baseline manifest must contain exactly two baselines")
for index, baseline in enumerate(baselines):
if type(baseline) is not dict or set(baseline) != {"manifest", "gregTechVersion"}:
fail(f"baseline {index} must contain only manifest and gregTechVersion")
for key in ("manifest", "gregTechVersion"):
value = baseline[key]
if type(value) is not str or SAFE_VALUE.fullmatch(value) is None:
fail(f"baseline {index} has unsafe {key}")
manifests = [baseline["manifest"] for baseline in baselines]
if len(set(manifests)) != len(manifests):
fail("baseline manifests must be unique")
properties = Path("gradle.properties").read_text(encoding="utf-8")
default_matches = re.findall(
r"(?m)^\s*elytra\.manifest\.version\s*=\s*([^\s#]+)\s*$",
properties,
)
if len(default_matches) != 1 or SAFE_VALUE.fullmatch(default_matches[0]) is None:
fail("gradle.properties must define one safe default manifest")
if manifests.count(default_matches[0]) != 1:
fail("default manifest must occur exactly once in the baseline manifest")
matrix = json.dumps({"include": baselines}, separators=(",", ":"))
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
print(f"matrix={matrix}", file=output)
PY
baseline:
name: GTNH ${{ matrix.manifest }}
needs: prepare-baselines
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
VERSION: 5.1.1-ci+${{ github.sha }}
strategy:
fail-fast: false
max-parallel: 2
matrix: ${{ fromJSON(needs.prepare-baselines.outputs.matrix) }}
steps:
- name: Checkout mod repo
uses: actions/checkout@v5
with:
fetch-depth: 32
submodules: recursive
- name: Determine JDK versions
id: list-jdk-versions
shell: bash
run: |
(
echo 'java-versions<<EOF'
echo 8
echo 17
echo 21
if [[ -f gradle/gradle-daemon-jvm.properties ]]; then
yq -pprops -oprops '.toolchainVersion' gradle/gradle-daemon-jvm.properties
fi
echo EOF
) | tee -a "${GITHUB_OUTPUT}"
- name: Set up JDK versions
uses: actions/setup-java@v5
with:
java-version: ${{ steps.list-jdk-versions.outputs.java-versions }}
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
with:
build-scan-publish: true
build-scan-terms-of-use-url: "https://gradle.com/terms-of-service"
build-scan-terms-of-use-agree: "yes"
cache-disabled: true
validate-wrappers: true
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Setup the workspace
run: ./gradlew --info --stacktrace "-Pelytra.manifest.version=${{ matrix.manifest }}" setupCIWorkspace
- name: Verify resolved GregTech baseline
run: ./gradlew --info --stacktrace "-Pelytra.manifest.version=${{ matrix.manifest }}" "-Pqz.gtnh.expectedGregTechVersion=${{ matrix.gregTechVersion }}" verifyGtnhBaseline
- name: Run tests
run: ./gradlew --info --stacktrace "-Pelytra.manifest.version=${{ matrix.manifest }}" test
- name: Run checks
run: ./gradlew --info --stacktrace "-Pelytra.manifest.version=${{ matrix.manifest }}" check
- name: Build the mod
run: ./gradlew --info --stacktrace "-Pelytra.manifest.version=${{ matrix.manifest }}" build
- name: Verify injected version in generated source and main JAR
shell: bash
run: |
python3 <<'PY'
import os
import re
import struct
import zipfile
from pathlib import Path
expected = f"5.1.1-ci+{os.environ['GITHUB_SHA']}"
declaration = re.compile(
r'^\s*public\s+static\s+final\s+String\s+VERSION\s*=\s*"([^"]+)"\s*;\s*$',
re.MULTILINE,
)
generated = []
for path in sorted(Path("build").rglob("Tags.java")):
values = declaration.findall(path.read_text(encoding="utf-8"))
generated.extend((path, value) for value in values)
if not generated:
raise SystemExit("generated Tags.VERSION declaration not found")
wrong = [(str(path), value) for path, value in generated if value != expected]
if wrong:
raise SystemExit(f"generated Tags.VERSION mismatch: {wrong!r}; expected {expected!r}")
jars = sorted(Path("build/libs").glob("*.jar"))
main_jars = [
path for path in jars
if not path.name.endswith(("-dev.jar", "-sources.jar", "-javadoc.jar"))
]
if len(main_jars) != 1:
raise SystemExit(f"expected one main JAR, found {[str(path) for path in main_jars]!r}")
with zipfile.ZipFile(main_jars[0]) as archive:
class_bytes = archive.read("club/heiqi/qz_miner/Tags.class")
if class_bytes[:4] != b"\xca\xfe\xba\xbe":
raise SystemExit("generated Tags.class has an invalid classfile header")
constants = []
offset = 8
constant_count = struct.unpack_from(">H", class_bytes, offset)[0]
offset += 2
index = 1
fixed_width = {3: 4, 4: 4, 7: 2, 8: 2, 9: 4, 10: 4, 11: 4,
12: 4, 15: 3, 16: 2, 17: 4, 18: 4, 19: 2, 20: 2}
while index < constant_count:
tag = class_bytes[offset]
offset += 1
if tag == 1:
length = struct.unpack_from(">H", class_bytes, offset)[0]
offset += 2
constants.append(class_bytes[offset:offset + length].decode("utf-8"))
offset += length
elif tag in (5, 6):
offset += 8
index += 1
elif tag in fixed_width:
offset += fixed_width[tag]
else:
raise SystemExit(f"unsupported constant-pool tag {tag}")
index += 1
if expected not in constants:
raise SystemExit(f"main JAR Tags.class does not contain exact version {expected!r}")
PY
build:
name: build
if: ${{ always() }}
needs: [ prepare-baselines, baseline ]
runs-on: ubuntu-24.04
steps:
- name: Require every baseline job to succeed
shell: bash
run: |
test '${{ needs.prepare-baselines.result }}' = 'success'
test '${{ needs.baseline.result }}' = 'success'