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
35 changes: 31 additions & 4 deletions .github/scripts/test_release_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
MATRIX113 = MATRIX112 + [origin + ' / ' + stamps for origin in
['released-112', 'recovery-112', 'via112-110', 'via112-111']
for stamps in ['normal', 'stamps']]
MATRIX114 = MATRIX113 + [origin + ' / ' + stamps for origin in
['released-113', 'recovery-113', 'via113-110', 'via113-111', 'via113-112']
for stamps in ['normal', 'stamps']]
MATRIX = [origin + ' / ' + stamps for origin in
['original-110', 'released-110', 'original-111', 'recovery-110']
for stamps in ['normal', 'stamps']]
Expand Down Expand Up @@ -53,7 +56,8 @@ def validate(self, minecraft, names=None, mutation=None, release_exists=False, w
'1.10.2': ('110021', '1.10.2', '12.18.3.2511'),
'1.11.2': ('111021', '1.11.2-dev', '13.20.1.2588'),
'1.12.2': ('112021', 'master-1.12', '14.23.5.2859'),
'1.13.2': ('113021', 'master-1.13.2', '25.0.223')}[minecraft]
'1.13.2': ('113021', 'master-1.13.2', '25.0.223'),
'1.14.4': ('114041', 'master-1.14.4', '28.2.26')}[minecraft]
version = '2.4.2.' + suffix
values = dict(minecraft_version=minecraft, forge_version=forge, loader_name='forge', loader_code='1',
java_version='8', java_toolchain_version='8.0.502+7', gradle_java_version='17', mod_id='lootablebodies',
Expand All @@ -62,7 +66,8 @@ def validate(self, minecraft, names=None, mutation=None, release_exists=False, w
if mutation:
values.update(mutation)
checks = CORE + {'1.11.2': MATRIX, '1.12.2': MATRIX112,
'1.13.2': MATRIX113 + ['Validate release workflow']}.get(minecraft, []) if names is None else names
'1.13.2': MATRIX113 + ['Validate release workflow'],
'1.14.4': MATRIX114 + ['Validate release workflow']}.get(minecraft, []) if names is None else names
def command(args, **options):
if args[0] == 'git':
return SHA + '\n'
Expand All @@ -85,16 +90,21 @@ def existing(args, **options):
patch('subprocess.check_output', side_effect=command), patch('subprocess.run', side_effect=existing):
exec(compile(code, 'release-preflight', 'exec'), {})

def test_future_115_candidate_cannot_dispatch_publication(self):
code, values = self.select('2.4.6.115021')
self.assertNotEqual(0, code)
self.assertEqual({}, values)

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')]:
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):
status, values = self.select('2.4.2.' + suffix)
self.assertEqual(0, status)
self.assertEqual(minecraft, values['minecraft_version'])
self.assertEqual(branch, values['source_branch'])

def test_invalid_versions_are_rejected(self):
for version in ['2.4.2', '2.4.2.114041', '02.4.2.111021', '2.4.2.111021;exit 0']:
for version in ['2.4.2', '2.4.2.114021', '02.4.2.111021', '2.4.2.111021;exit 0']:
with self.subTest(version=version):
self.assertNotEqual(0, self.select(version)[0])

Expand Down Expand Up @@ -143,6 +153,23 @@ def test_113_audit_receives_the_registered_shared_ci_completion(self):
event_names = audit.split('workflows: [', 1)[1].split(']', 1)[0].split(',')
self.assertIn('LootableBodies 1.12 CI', [name.strip() for name in event_names])

def test_114_requires_every_upgrade_case_and_workflow_validation(self):
self.validate('1.14.4')
required = MATRIX114 + ['Validate release workflow']
self.assertEqual(34, len(MATRIX114))
for missing in required:
with self.subTest(missing=missing), self.assertRaises(SystemExit):
self.validate('1.14.4', names=CORE + [name for name in required if name != missing])

def test_114_requires_an_independent_audit_of_its_exact_artifact(self):
with self.assertRaises(SystemExit):
self.validate('1.14.4', audit_success=False)

def test_114_audit_receives_the_registered_shared_ci_completion(self):
audit = WORKFLOW.with_name('artifact-audit-1.14.yml').read_text(encoding='utf-8')
event_names = audit.split('workflows: [', 1)[1].split(']', 1)[0].split(',')
self.assertIn('LootableBodies 1.12 CI', [name.strip() for name in event_names])

def test_identity_changes_are_rejected(self):
for mutation in [{'mod_id': 'other'}, {'minecraft_version': '1.10.2'}, {'mod_license': 'CC0-1.0'}]:
with self.subTest(mutation=mutation), self.assertRaises(SystemExit):
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/artifact-audit-1.14.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Audit LootableBodies 1.14 artifacts
run-name: Audit 1.14.4 artifacts from ${{ github.event.workflow_run.head_sha }}

on:
workflow_run:
workflows: [LootableBodies 1.12 CI, LootableBodies 1.14 CI]
types: [completed]

permissions:
contents: read
actions: read

jobs:
audit:
# Pull-request merge trees are audited inside CI. Push artifacts use the
# workflow run's exact head SHA, which is also their declared artifact name.
if: >-
github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' &&
(github.event.workflow_run.head_branch == 'master-1.14.4')
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.workflow_run.head_sha }}
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '25.0.3+9.0.LTS'
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '8.0.502+7'
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '17.0.1+12'
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: LootableBodies-1.14.4-${{ github.event.workflow_run.head_sha }}
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
path: downloaded
- name: Require exact inputs and assemble an immutable audit bundle
run: |
set -euo pipefail
mkdir audited
for name in LootableBodies-2.4.5.114041.jar LootableBodies-2.4.5.114041-sources.jar LootableBodies-2.4.5.114041-javadoc.jar; do
test -s "downloaded/build/libs/$name"
cp "downloaded/build/libs/$name" audited/
done
test -s downloaded/build/release/SHA256SUMS
test -s downloaded/build/publications/mavenJava/pom-default.xml
test -s downloaded/CHANGELOG.txt
cp downloaded/build/release/SHA256SUMS audited/
cp downloaded/build/publications/mavenJava/pom-default.xml audited/LootableBodies-2.4.5.114041.pom
cp downloaded/CHANGELOG.txt audited/
- name: Audit original bytes without recompiling the jars
env:
LOOTABLEBODIES_JAVA8_HOME: ${{ env.JAVA_HOME_8_X64 }}
LOOTABLEBODIES_MAVENIZER_JAVA_HOME: ${{ env.JAVA_HOME_25_X64 }}
run: |
chmod +x ./gradlew
./gradlew verifyPreparedReleaseArtifacts -PpreparedReleaseDir=audited --no-daemon
18 changes: 11 additions & 7 deletions .github/workflows/deploy-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
workflow_dispatch:
inputs:
release_version:
description: Complete version from gradle.properties, ending in 110021, 111021, 112021 or 113021.
description: Complete version from gradle.properties, ending in 110021, 111021, 112021, 113021 or 114041.
required: true
type: string
curseforge_release_level:
Expand Down Expand Up @@ -48,12 +48,13 @@ jobs:
set -euo pipefail
[[ "$GITHUB_REPOSITORY" == 'MinecraftModDevelopmentMods/LootableBodies' ]]
[[ "$GITHUB_REF" == 'refs/heads/master-1.12' ]]
[[ "$RELEASE_VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(110021|111021|112021|113021)$ ]]
[[ "$RELEASE_VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(110021|111021|112021|113021|114041)$ ]]
case "$RELEASE_VERSION" in
*.110021) minecraft_version=1.10.2; source_branch=1.10.2 ;;
*.111021) minecraft_version=1.11.2; source_branch=1.11.2-dev ;;
*.112021) minecraft_version=1.12.2; source_branch=master-1.12 ;;
*.113021) minecraft_version=1.13.2; source_branch=master-1.13.2 ;;
*.114041) minecraft_version=1.14.4; source_branch=master-1.14.4 ;;
*) exit 1 ;;
esac
echo "minecraft_version=$minecraft_version" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -81,7 +82,8 @@ jobs:
targets = {'1.10.2': ('1.10.2', '12.18.3.2511', '110021'),
'1.11.2': ('1.11.2-dev', '13.20.1.2588', '111021'),
'1.12.2': ('master-1.12', '14.23.5.2859', '112021'),
'1.13.2': ('master-1.13.2', '25.0.223', '113021')}
'1.13.2': ('master-1.13.2', '25.0.223', '113021'),
'1.14.4': ('master-1.14.4', '28.2.26', '114041')}
branch, forge, suffix = targets[minecraft]
if os.environ['SOURCE_BRANCH'] != branch:
raise SystemExit('The selected branch does not match its Minecraft version')
Expand All @@ -101,19 +103,21 @@ jobs:
checks = json.loads(subprocess.check_output(['gh', 'api', f'repos/{repo}/commits/{sha}/check-runs?per_page=100'], text=True))['check_runs']
required = {'Cold Forge bootstrap', 'Build, test, and audit', 'Analyze Java', 'Validation'}
origins = ['original-110', 'released-110', 'original-111', 'recovery-110']
if minecraft in ('1.12.2', '1.13.2'):
if minecraft in ('1.12.2', '1.13.2', '1.14.4'):
origins += ['released-111', 'original-112', 'recovery-111', 'via111-110']
if minecraft == '1.13.2':
if minecraft in ('1.13.2', '1.14.4'):
origins += ['released-112', 'recovery-112', 'via112-110', 'via112-111']
required.add('Validate release workflow')
if minecraft in ('1.11.2', '1.12.2', '1.13.2'):
if minecraft == '1.14.4':
origins += ['released-113', 'recovery-113', 'via113-110', 'via113-111', 'via113-112']
if minecraft in ('1.11.2', '1.12.2', '1.13.2', '1.14.4'):
required.update(origin + ' / ' + stamps for origin in origins
for stamps in ['normal', 'stamps'])
for name in required:
matching = [check for check in checks if check['name'] == name]
if not matching or max(matching, key=lambda c: c['id'])['conclusion'] != 'success':
raise SystemExit(f'The selected commit has no successful {name} check')
if minecraft in ('1.11.2', '1.12.2', '1.13.2'):
if minecraft in ('1.11.2', '1.12.2', '1.13.2', '1.14.4'):
target = minecraft.rsplit('.', 1)[0]
audits = json.loads(subprocess.check_output(['gh', 'api',
f'repos/{repo}/actions/workflows/artifact-audit-{target}.yml/runs?event=workflow_run&per_page=100'], text=True))['workflow_runs']
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-workflow-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Check release workflow syntax
run: >-
"$RUNNER_TEMP/actionlint/actionlint" -shellcheck= -pyflakes=
.github/workflows/deploy-release.yml .github/workflows/release-workflow-checks.yml .github/workflows/artifact-audit-1.11.yml .github/workflows/artifact-audit-1.12.yml .github/workflows/artifact-audit-1.13.yml
.github/workflows/deploy-release.yml .github/workflows/release-workflow-checks.yml .github/workflows/artifact-audit-1.11.yml .github/workflows/artifact-audit-1.12.yml .github/workflows/artifact-audit-1.13.yml .github/workflows/artifact-audit-1.14.yml

- name: Test version selection and publication checks
run: python3 .github/scripts/test_release_workflow.py -v
Loading