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
18 changes: 9 additions & 9 deletions .github/release120.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"version": "2.4.13.121011",
"minecraft": "1.21.1",
"forge": "52.1.20",
"version": "2.4.14.121111",
"minecraft": "1.21.11",
"forge": "61.2.4",
"local_qualification_complete": true,
"qualification_integrity_status": "verified",
"manual_accepted": true,
"files": {
"CHANGELOG.txt": "A7A09C8F1C6CEC52E4861B97AE8D4637C45054931F2D8D07ADC60F503E3561F3",
"LootableBodies-2.4.13.121011-javadoc.jar": "4DA01E1F17CA8D17F41DA5BE15C6513359B64598167A467096E6E5992AA1270B",
"LootableBodies-2.4.13.121011-sources.jar": "EAFC9F737A70846A99A96D625C38485ED82248D74E7D907D62FF02F63622BEDD",
"LootableBodies-2.4.13.121011.jar": "F24183DF61AB677017915CE0D0B17816D6D6A6C095BC6FBF6E40070E33DC1845",
"LootableBodies-2.4.13.121011.pom": "4FAC990CA9B74FB3AEB8B734EFBEE8461D84682C33ACCB95B8DD4A7E0443E272",
"SHA256SUMS": "147108294CEAA421090E75CFBDBC537E7F6E087DB116A1638783D65C89739486"
"CHANGELOG.txt": "2FE5E10AF26A39BB3684983EEFEF739F3C25E847BC543F08D9D46B6758EA391D",
"LootableBodies-2.4.14.121111-javadoc.jar": "B9D9C52A62928073028DD695549AE39B8CACAF256E7428FBDA773EE3E7309462",
"LootableBodies-2.4.14.121111-sources.jar": "C23B401E5AA952F59AB55B34E4A23B9F2A19289D961BFCE6E104E6A505C91902",
"LootableBodies-2.4.14.121111.jar": "273FF5DA71743CFA883B036DD82B9C6D5F85CB8302791D71BD5384674A22CC71",
"LootableBodies-2.4.14.121111.pom": "D94861900E3523B689DDA074350DE75DF634BAA2D101426EE03CF19EA2570B5D",
"SHA256SUMS": "2240EB872764D99467D4217E4F49B3D720137BF68032784B43FFB2AD8593BEFF"
}
}
87 changes: 75 additions & 12 deletions .github/scripts/test_release_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def select(self, version, repository=REPOSITORY, ref='refs/heads/master-1.12'):
values = dict(line.split('=', 1) for line in output.read_text().splitlines()) if output.exists() else {}
return result.returncode, values

def validate(self, minecraft, names=None, mutation=None, release_exists=False, wrong_tag=False, audit_success=True, qualification=None, local_receipt=None):
def validate(self, minecraft, names=None, mutation=None, release_exists=False, wrong_tag=False, audit_success=True, qualification=None, local_receipt=None, audit_run=None):
suffix, branch, forge = {
'1.10.2': ('110021', '1.10.2', '12.18.3.2511'),
'1.11.2': ('111021', '1.11.2-dev', '13.20.1.2588'),
Expand All @@ -73,13 +73,14 @@ def validate(self, minecraft, names=None, mutation=None, release_exists=False, w
'1.19.4': ('119041', 'master-1.19.4', '45.4.5'),
'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')}[minecraft]
'1.21.1': ('121011', 'master-1.21.1', '52.1.20'),
'1.21.11': ('121111', 'master-1.21.11', '61.2.4')}[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') 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') 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') else '17', mod_id='lootablebodies',
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',
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 @@ -89,19 +90,24 @@ 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') 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') 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'}[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'}[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]:
return json.dumps({'workflow_runs': [{'id': 1, 'display_title': 'Audit ' + minecraft + ' artifacts from ' + SHA,
'conclusion': 'success' if audit_success else 'failure'}]})
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'}
if audit_run is not None:
audit.update(audit_run)
return json.dumps({'workflow_runs': [audit]})
return json.dumps({'check_runs': [dict(name=name, id=i, conclusion='success') for i, name in enumerate(checks)]})
def existing(args, **options):
if '/releases/tags/' in args[-1] and release_exists:
Expand All @@ -112,7 +118,7 @@ def existing(args, **options):
code = script('Validate identity, exact commit and successful checks').split("python3 - <<'PY'\n", 1)[1].rsplit('\nPY', 1)[0]
with tempfile.TemporaryDirectory() as directory, patch.dict(os.environ, {
'RELEASE_VERSION': version, 'GITHUB_REPOSITORY': REPOSITORY,
'MINECRAFT_VERSION': minecraft, 'SOURCE_BRANCH': branch,
'MINECRAFT_VERSION': minecraft, 'SOURCE_BRANCH': branch, 'GITHUB_SHA': 'c' * 40,
'GITHUB_OUTPUT': str(Path(directory) / 'output'),
'LOCAL_QUALIFICATION':json.dumps(receipt) if receipt else ''}), \
patch('pathlib.Path.read_text', side_effect=['\n'.join(k+'='+v for k,v in values.items()),json.dumps({'files':files,'local_qualification_complete':True,'qualification_integrity_status':'verified','manual_accepted':True})]), \
Expand All @@ -129,8 +135,8 @@ def test_1206_requires_exact_archives_and_java_21(self):
{'local_receipt': {'manual_accepted': False}}):
with self.subTest(options=options), self.assertRaises(SystemExit):
self.validate('1.20.6', **options)
self.assertIn("(needs.preflight.outputs.minecraft_version == '1.20.6' || needs.preflight.outputs.minecraft_version == '1.21.1') && 'Java 21'", WORKFLOW.read_text())
self.assertEqual(2, WORKFLOW.read_text().count("(needs.preflight.outputs.minecraft_version == '1.20.6' || needs.preflight.outputs.minecraft_version == '1.21.1') && '21.0.7+6.0.LTS'"))
self.assertIn("(needs.preflight.outputs.minecraft_version == '1.20.6' || needs.preflight.outputs.minecraft_version == '1.21.1' || needs.preflight.outputs.minecraft_version == '1.21.11') && 'Java 21'", WORKFLOW.read_text())
self.assertEqual(2, WORKFLOW.read_text().count("(needs.preflight.outputs.minecraft_version == '1.20.6' || needs.preflight.outputs.minecraft_version == '1.21.1' || needs.preflight.outputs.minecraft_version == '1.21.11') && '21.0.7+6.0.LTS'"))

def test_1211_requires_exact_archives_and_java_21(self):
code, values = self.select('2.4.13.121011')
Expand All @@ -143,8 +149,65 @@ def test_1211_requires_exact_archives_and_java_21(self):
with self.subTest(options=options), self.assertRaises(SystemExit):
self.validate('1.21.1', **options)

def test_future_12111_candidate_cannot_dispatch_publication(self):
self.assertNotEqual(0, self.select('2.4.14.121111')[0])
def test_1211_audit_receives_registered_archive_workflow_completion(self):
audit = WORKFLOW.with_name('artifact-audit-1.21.yml').read_text(encoding='utf-8')
event_names = audit.split('workflows: [', 1)[1].split(']', 1)[0].split(',')
self.assertIn('LootableBodies 1.20 CI', [name.strip() for name in event_names])

def test_1211_only_accepts_an_audit_from_the_active_dispatcher(self):
self.validate('1.21.1', audit_run={'event': 'workflow_dispatch'})
for changes in ({'event': 'push'}, {'head_branch': 'master-1.21.1'},
{'head_sha': 'd' * 40}, {'path': '.github/workflows/artifact-audit-1.20.yml'}):
with self.subTest(changes=changes), self.assertRaises(SystemExit):
self.validate('1.21.1', audit_run=changes)

def test_1211_audit_requires_the_successful_current_source_push(self):
path = WORKFLOW.with_name('artifact-audit-1.21.yml')
text = path.read_text(encoding='utf-8')
code = text.split("python3 - <<'PY'\n", 1)[1].split('\n PY', 1)[0]
code = textwrap.dedent(code)
good = {'id': 25, 'head_repository': {'full_name': REPOSITORY}, 'event': 'push',
'head_branch': 'master-1.21.1', 'head_sha': SHA, 'path': '.github/workflows/native120.yml',
'status': 'completed', 'conclusion': 'success'}

def resolve(run, branch=SHA, run_id='25', source=SHA, target='1.21.1'):
def api(args):
if args[-1].endswith('/actions/runs/25'):
return json.dumps(run)
if args[-1].endswith('/git/ref/heads/master-' + target):
return json.dumps({'object': {'sha': branch}})
raise AssertionError(args)
with tempfile.TemporaryDirectory() as folder, patch('subprocess.check_output', side_effect=api):
output = Path(folder) / 'output'
with patch.dict(os.environ, SOURCE_RUN=run_id, SOURCE_SHA=source, TARGET_VERSION=target, GITHUB_OUTPUT=str(output)):
exec(compile(code, 'audit-source', 'exec'), {})
return output.read_text()

self.assertEqual('run=25\nsha=' + SHA + '\nversion=1.21.1\n', resolve(good))
self.assertEqual('run=25\nsha=' + SHA + '\nversion=1.21.11\n',
resolve(dict(good, head_branch='master-1.21.11'), target='1.21.11'))
with self.assertRaises(SystemExit):
resolve(good, target='1.21.11')
for changes in ({'event': 'pull_request'}, {'head_sha': 'b' * 40}, {'head_branch': 'master-1.20.6'},
{'head_repository': {'full_name': 'SkyBlade1978/LootableBodies'}},
{'path': '.github/workflows/native119.yml'}, {'conclusion': 'failure'}, {'id': 26}):
with self.subTest(changes=changes), self.assertRaises(SystemExit):
resolve(dict(good, **changes))
with self.assertRaises(SystemExit):
resolve(good, branch='b' * 40)
with self.assertRaises(SystemExit):
resolve(good, run_id='25;false')

def test_12111_requires_the_exact_archive_and_dispatcher(self):
self.assertEqual((0, {'minecraft_version': '1.21.11', 'source_branch': 'master-1.21.11'}),
self.select('2.4.14.121111'))
self.validate('1.21.11', audit_run={'event': 'workflow_dispatch'})
for options in ({'audit_success': False}, {'qualification': {'head_branch': 'master-1.21.1'}},
{'audit_run': {'head_sha': 'd' * 40}}, {'mutation': {'java_version': '17'}},
{'local_receipt': {'manual_accepted': False}}):
with self.subTest(options=options), self.assertRaises(SystemExit):
self.validate('1.21.11', **options)
self.assertNotEqual(0, self.select('2.4.15.261021')[0])

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')]:
Expand Down
71 changes: 62 additions & 9 deletions .github/workflows/artifact-audit-1.21.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,86 @@
name: Audit LootableBodies 1.21 artifacts
run-name: Audit 1.21.1 artifacts from ${{ github.event.workflow_run.head_sha }}
run-name: Audit ${{ github.event.workflow_run.head_branch == 'master-1.21.11' && '1.21.11' || inputs.minecraft_version || '1.21.1' }} artifacts from ${{ github.event.workflow_run.head_sha || inputs.source_sha }}
on:
workflow_run:
workflows: [LootableBodies 1.21.1 CI]
workflows: [LootableBodies 1.20 CI, LootableBodies 1.20.6 CI, LootableBodies 1.21.1 CI, LootableBodies 1.21.11 CI]
types: [completed]
workflow_dispatch:
inputs:
minecraft_version:
description: Minecraft version of the merged archive build
type: choice
default: '1.21.1'
options: ['1.21.1', '1.21.11']
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.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'master-1.21.1' && github.event.workflow_run.name == 'LootableBodies 1.21.1 CI'
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-1.21.1' && github.event.workflow_run.name == 'LootableBodies 1.21.1 CI') ||
(github.event.workflow_run.head_branch == 'master-1.21.11' && github.event.workflow_run.name == 'LootableBodies 1.21.11 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: ${{ github.event.workflow_run.head_branch == 'master-1.21.11' && '1.21.11' || inputs.minecraft_version || '1.21.1' }}
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', '1.21.1')
if target not in ('1.21.1', '1.21.11'):
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: ${{ github.event.workflow_run.head_sha }}
ref: ${{ steps.source.outputs.sha }}
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: LootableBodies-1.21.1-${{ github.event.workflow_run.head_sha }}
run-id: ${{ github.event.workflow_run.id }}
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: ${{ github.event.workflow_run.head_sha }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
python3 -B tools/native120_build.py --bundle audited
python3 - <<'PY'
Expand All @@ -43,6 +96,6 @@ jobs:
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: native120-independent-audit-${{ github.event.workflow_run.head_sha }}
name: native120-independent-audit-${{ steps.source.outputs.sha }}
if-no-files-found: error
path: independent-audit.json
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: CodeQL
on:
push:
branches:
- master-1.21.1
- master-1.21.11
- 'feature/**'
pull_request:
branches:
- master-1.21.1
- master-1.21.11
schedule:
- cron: '43 7 * * 4'

Expand Down
Loading
Loading