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
2 changes: 1 addition & 1 deletion .github/scripts/release_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def runtime_required(paths,manual=False,workflow_pairs=None):
'.github/scripts/test_release_workflow.py','.github/scripts/release_scope.py','.github/scripts/test_release_scope.py'}
pairs=workflow_pairs or {}
for name in paths:
if name in allowed or re.fullmatch(r'\.github/workflows/artifact-audit-1\.\d+\.yml',name):continue
if name in allowed or re.fullmatch(r'\.github/workflows/artifact-audit-(?:1\.\d+|26\.1)\.yml',name):continue
if name not in ('.github/workflows/build.yml','.github/workflows/migration-matrix.yml') or name not in pairs:
return True
before,after=pairs[name]
Expand Down
4 changes: 4 additions & 0 deletions .github/scripts/test_release_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ def test_only_release_routes_use_the_small_checks(self):
self.assertFalse(runtime_required(['.github/workflows/deploy-release.yml','.github/scripts/test_release_workflow.py']))
def test_new_target_audit_uses_the_small_checks(self):
self.assertFalse(runtime_required(['.github/workflows/artifact-audit-1.16.yml']))
def test_native26_artifact_audit_changes_do_not_launch_old_games(self):
self.assertFalse(runtime_required(['.github/workflows/artifact-audit-26.1.yml']))
for path in ['.github/workflows/artifact-audit-26.1.yml.bak', '.github/workflows/unreviewed-26.1.yml']:
with self.subTest(path=path):self.assertTrue(runtime_required([path]))
def test_source_build_and_unknown_files_require_runtime(self):
for name in ['src/main/java/Corpse.java','src/test/java/CorpseTest.java','tools/fixture_login.py','gradle/release.gradle','gradle.properties','build.gradle','README.md','settings.gradle','.github/workflows/codeql-analysis.yml','other.txt']:
with self.subTest(name=name):self.assertTrue(runtime_required([name]))
Expand Down
25 changes: 20 additions & 5 deletions .github/scripts/test_release_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ def validate(self, minecraft, names=None, mutation=None, release_exists=False, w
'1.20.1': ('120011', 'master-1.20.1', '47.4.28'),
'1.20.6': ('120061', 'master-1.20.6', '50.2.14'),
'1.21.1': ('121011', 'master-1.21.1', '52.1.20'),
'1.21.11': ('121111', 'master-1.21.11', '61.2.4')}[minecraft]
'1.21.11': ('121111', 'master-1.21.11', '61.2.4'),
'26.1.2': ('2601021', 'master-26.1.2', '64.1.5')}[minecraft]
version = '2.4.2.' + suffix
files={name:'C'*64 for name in ['main.jar','sources.jar','javadoc.jar','release.pom','CHANGELOG.txt','SHA256SUMS']}
receipt={'schema':1,'source':SHA,'version':version,'files':files,'evidence_sha256':'F'*64,'qualified_locally':True,'manual_accepted':True}
if local_receipt is not None:receipt=local_receipt
values = dict(minecraft_version=minecraft, forge_version=forge, loader_name='forge', loader_code='1',
java_version='21' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17' if minecraft in ('1.18.2','1.19.4','1.20.1') else '16' if minecraft=='1.17.1' else '8', java_toolchain_version='21.0.7+6' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17.0.20.1+1' if minecraft in ('1.18.2','1.19.4','1.20.1') else '16.0.2+7' if minecraft=='1.17.1' else '8.0.502+7', gradle_java_version='21' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17', mod_id='lootablebodies',
java_version='25' if minecraft=='26.1.2' else '21' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17' if minecraft in ('1.18.2','1.19.4','1.20.1') else '16' if minecraft=='1.17.1' else '8', java_toolchain_version='25.0.3+9' if minecraft=='26.1.2' else '21.0.7+6' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17.0.20.1+1' if minecraft in ('1.18.2','1.19.4','1.20.1') else '16.0.2+7' if minecraft=='1.17.1' else '8.0.502+7', gradle_java_version='25' if minecraft=='26.1.2' else '21' if minecraft in ('1.20.6','1.21.1','1.21.11') else '17', mod_id='lootablebodies',
mod_artifact='LootableBodies', mod_group='zone.moddev.mc.lootablebodies', mod_license='LGPL-2.1-only',
curseforge_project_id='240968', mod_version=version)
if mutation:
Expand All @@ -90,21 +91,21 @@ def validate(self, minecraft, names=None, mutation=None, release_exists=False, w
'1.14.4': MATRIX114 + ['Validate release workflow'],
'1.15.2': MATRIX115, '1.16.5': MATRIX116}.get(minecraft, []) if names is None else names
if minecraft=='1.17.1' and names is None:checks=CORE117+MATRIX117
if minecraft in ('1.18.2','1.19.4','1.20.1','1.20.6','1.21.1','1.21.11') and names is None:checks=CORE118
if minecraft in ('1.18.2','1.19.4','1.20.1','1.20.6','1.21.1','1.21.11','26.1.2') and names is None:checks=CORE118

def command(args, **options):
if args[0] == 'git':
return SHA + '\n'
if any('/actions/workflows/native'+code+'.yml/' in args[-1] for code in ['117','118','119','120']):
code={'1.17.1':'117','1.18.2':'118','1.19.4':'119','1.20.1':'120','1.20.6':'120','1.21.1':'120','1.21.11':'120'}[minecraft]
code={'1.17.1':'117','1.18.2':'118','1.19.4':'119','1.20.1':'120','1.20.6':'120','1.21.1':'120','1.21.11':'120','26.1.2':'120'}[minecraft]
row=dict(id=int(code),head_sha=SHA,head_branch=branch,event='push',path='.github/workflows/native'+code+'.yml',conclusion='success')
if qualification:row.update(qualification)
return json.dumps({'workflow_runs':[row]})
if '/actions/workflows/' in args[-1]:
audit = {'id': 1, 'display_title': 'Audit ' + minecraft + ' artifacts from ' + SHA,
'conclusion': 'success' if audit_success else 'failure', 'event': 'workflow_run',
'head_sha': 'c' * 40, 'head_branch': 'master-1.12',
'path': '.github/workflows/artifact-audit-1.21.yml'}
'path': '.github/workflows/artifact-audit-26.1.yml' if minecraft=='26.1.2' else '.github/workflows/artifact-audit-1.21.yml'}
if audit_run is not None:
audit.update(audit_run)
return json.dumps({'workflow_runs': [audit]})
Expand Down Expand Up @@ -209,6 +210,20 @@ def test_12111_requires_the_exact_archive_and_dispatcher(self):
self.validate('1.21.11', **options)
self.assertNotEqual(0, self.select('2.4.15.261021')[0])

def test_2612_uses_java25_exact_archives_and_current_dispatcher(self):
self.assertEqual((0, {'minecraft_version':'26.1.2','source_branch':'master-26.1.2'}), self.select('2.4.15.2601021'))
self.validate('26.1.2', audit_run={'event':'workflow_dispatch'})
for options in ({'mutation':{'java_version':'21'}}, {'mutation':{'java_toolchain_version':'25.0.2+10'}},
{'audit_success':False}, {'qualification':{'head_branch':'master-1.21.11'}},
{'local_receipt':{'manual_accepted':False}}, {'audit_run':{'head_sha':'d'*40}},
{'audit_run':{'path':'.github/workflows/artifact-audit-1.21.yml'}}):
with self.subTest(options=options), self.assertRaises(SystemExit):self.validate('26.1.2', **options)
workflow=WORKFLOW.read_text()
self.assertIn("needs.preflight.outputs.minecraft_version == '26.1.2' && 'Java 25'",workflow)
for line in workflow.splitlines():
if 'if: needs.preflight.outputs.minecraft_version !=' in line:
self.assertIn("!= '26.1.2'",line)

def test_routes_all_supported_versions(self):
for suffix, minecraft, branch in [('110021', '1.10.2', '1.10.2'), ('111021', '1.11.2', '1.11.2-dev'), ('112021', '1.12.2', 'master-1.12'), ('113021', '1.13.2', 'master-1.13.2'), ('114041', '1.14.4', 'master-1.14.4')]:
with self.subTest(minecraft=minecraft):
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/artifact-audit-26.1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Audit LootableBodies 26.1 artifacts
run-name: Audit 26.1.2 artifacts from ${{ github.event.workflow_run.head_sha || inputs.source_sha }}
on:
workflow_run:
workflows: [LootableBodies 1.20 CI, LootableBodies 26.1.2 CI]
types: [completed]
workflow_dispatch:
inputs:
minecraft_version:
description: Minecraft version of the merged archive build
type: choice
default: '26.1.2'
options: ['26.1.2']
source_run:
description: Successful archive build run ID
required: true
type: string
source_sha:
description: Exact merged source SHA
required: true
type: string
permissions:
contents: read
actions: read
jobs:
audit:
if: >-
github.repository == 'MinecraftModDevelopmentMods/LootableBodies' &&
github.ref == 'refs/heads/master-1.12' &&
(github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' &&
(github.event.workflow_run.head_branch == 'master-26.1.2' && github.event.workflow_run.name == 'LootableBodies 26.1.2 CI')))
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Verify the exact merged archive build
id: source
env:
GH_TOKEN: ${{ github.token }}
TARGET_VERSION: ${{ inputs.minecraft_version || '26.1.2' }}
SOURCE_RUN: ${{ github.event.workflow_run.id || inputs.source_run }}
SOURCE_SHA: ${{ github.event.workflow_run.head_sha || inputs.source_sha }}
run: |
python3 - <<'PY'
import json, os, re, subprocess

repo = 'MinecraftModDevelopmentMods/LootableBodies'
run_id = os.environ['SOURCE_RUN']
source = os.environ['SOURCE_SHA']
if not re.fullmatch(r'[1-9][0-9]*', run_id) or not re.fullmatch(r'[a-f0-9]{40}', source):
raise SystemExit('An exact build run ID and source SHA are required')

def api(path):
return json.loads(subprocess.check_output(['gh', 'api', f'repos/{repo}/{path}']))

run = api(f'actions/runs/{run_id}')
target = os.environ.get('TARGET_VERSION', '26.1.2')
if target not in ('26.1.2',):
raise SystemExit('Unsupported archive audit target')
source_branch = 'master-' + target
branch = api('git/ref/heads/' + source_branch)
if (str(run['id']) != run_id or run['head_repository']['full_name'] != repo or
run['event'] != 'push' or run['head_branch'] != source_branch or run['head_sha'] != source or
run['path'] != '.github/workflows/native120.yml' or run['status'] != 'completed' or
run['conclusion'] != 'success' or branch['object']['sha'] != source):
raise SystemExit('The selected run is not the successful build of the current merged source')
with open(os.environ['GITHUB_OUTPUT'], 'a') as out:
out.write(f'run={run_id}\nsha={source}\nversion={target}\n')
PY
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ steps.source.outputs.sha }}
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: LootableBodies-${{ steps.source.outputs.version }}-${{ steps.source.outputs.sha }}
run-id: ${{ steps.source.outputs.run }}
github-token: ${{ github.token }}
path: audited
- name: Independently verify every locally qualified byte
env:
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
python3 -B tools/native120_build.py --bundle audited
python3 - <<'PY'
import json,os
from pathlib import Path
accepted=json.loads(Path('.github/release120.json').read_text())
Path('independent-audit.json').write_text(json.dumps({'source':os.environ['SOURCE_SHA'],'files':accepted['files'],'all_six_exact':True,'identical_to_locally_audited_bundle':True},indent=2)+'\n')
PY
# Exact hashes reuse the local classfile/licence/identity/mapping audit.
# This independent receipt is a byte match, not a fresh gameplay run or
# another Gradle/Forge preparation. The build job audits new archives.
- name: Retain independent audit evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: native120-independent-audit-${{ steps.source.outputs.sha }}
if-no-files-found: error
path: independent-audit.json
Loading
Loading