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
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ jobs:
-Dorg.gradle.java.installations.auto-detect=false \
-Dorg.gradle.java.installations.auto-download=false

- name: Stage and check immutable Maven publication
run: |
set -euo pipefail
java_args=(
"-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
)
./gradlew prepareReleaseBundle --no-daemon --stacktrace "${java_args[@]}"
./gradlew verifyPreparedMavenPublication \
-PpreparedReleaseDir="$GITHUB_WORKSPACE/build/prepared-release" \
--no-daemon --stacktrace "${java_args[@]}"

- name: Generate and audit Eclipse production launches
run: >-
./gradlew genEclipseRuns eclipse isolateEclipseProductionRuns verifyEclipseProductionClasspath
Expand Down
242 changes: 242 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
name: Resume prepared Forge 1.14 release

run-name: Resume IronAgeFurniture 0.4.0.114041 publication

on:
workflow_dispatch:
inputs:
confirm_live_publication:
description: Publish the original 0.4.0.114041 bundle without rebuilding or moving its tag.
required: true
default: false
type: boolean

permissions:
actions: read
contents: read

concurrency:
group: iron-age-furniture-release-0.4.0.114041
cancel-in-progress: false

# The first publication stopped during Gradle configuration, before any upload.
# This recovery is deliberately limited to that run and its unchanged artifacts.
# New releases still use the dispatcher on the repository's default branch.
env:
SOURCE_RUN: '37925871159'
SOURCE_ARTIFACT: '11613863502'
RELEASE_SHA: b9f06402602a677b249fbde7705973d9791e2db2
RELEASE_VERSION: '0.4.0.114041'

jobs:
validate:
name: Validate immutable publication recovery
runs-on: ubuntu-latest
timeout-minutes: 10
environment: release
steps:
- name: Check out publication repair
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.sha }}
fetch-depth: 0

- name: Verify confirmation, original run, tag and repair scope
env:
GH_TOKEN: ${{ github.token }}
CONFIRM_LIVE_PUBLICATION: ${{ inputs.confirm_live_publication }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }}
MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }}
MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }}
shell: bash
run: |
set -euo pipefail
[[ "$GITHUB_REPOSITORY" == MinecraftModDevelopmentMods/IronAgeFurniture ]]
[[ "$GITHUB_REF" == refs/heads/master-1.14 ]]
[[ "$CONFIRM_LIVE_PUBLICATION" == true ]]
for credential in CURSEFORGE_TOKEN MAVEN_UPLOAD_URL MAVEN_UPLOAD_USERNAME MAVEN_UPLOAD_PASSWORD; do
[[ -n "${!credential:-}" ]] || { echo "Missing release credential: $credential" >&2; exit 1; }
done
[[ "$(sed -n 's/^mod_version=//p' gradle.properties)" == "$RELEASE_VERSION" ]]
[[ "$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_VERSION" --jq .object.sha)" == "$RELEASE_SHA" ]]
gh api "repos/$GITHUB_REPOSITORY/actions/runs/$SOURCE_RUN" > "$RUNNER_TEMP/source-run.json"
jq -e '.event == "workflow_dispatch" and .status == "completed" and .conclusion == "failure" and .path == ".github/workflows/publish-release.yml"' "$RUNNER_TEMP/source-run.json"
gh api "repos/$GITHUB_REPOSITORY/actions/runs/$SOURCE_RUN/jobs?per_page=100" > "$RUNNER_TEMP/source-jobs.json"
jq -e '[.jobs[] | select(.name == "Build immutable release artifact" or .name == "Confirm 0.4.0.114041 live publication" or .name == "Create validated release tag") | .conclusion] == ["success", "success", "success"]' "$RUNNER_TEMP/source-jobs.json"
jq -e '[.jobs[] | select(.name == "Publish Maven") | .conclusion] == ["failure"] and [.jobs[] | select(.name == "Publish CurseForge" or .name == "Publish GitHub Release") | .conclusion] == ["skipped", "skipped"]' "$RUNNER_TEMP/source-jobs.json"
gh api "repos/$GITHUB_REPOSITORY/actions/artifacts/$SOURCE_ARTIFACT" > "$RUNNER_TEMP/source-artifact.json"
jq -e --argjson run "$SOURCE_RUN" '.expired == false and .workflow_run.id == $run and .name == "iron-age-furniture-1.14.4-forge-b9f06402602a677b249fbde7705973d9791e2db2-37925871159-1" and .digest == "sha256:f1d27dce09288bca21929f4c6bb6a1ed01bdbaa5b4e9f4a614e46c3af3cc1bd3"' "$RUNNER_TEMP/source-artifact.json"
checks="$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?per_page=100")"
for name in 'Build, test, and audit' 'Cold Forge bootstrap' 'Analyze Java' 'Validation'; do
jq -e --arg name "$name" '[.check_runs[] | select(.name == $name and .conclusion == "success")] | length > 0' <<<"$checks"
done
for changed in $(git diff --name-only "$RELEASE_SHA" "$GITHUB_SHA"); do
case "$changed" in
gradle/release/publishing.gradle|.github/workflows/ci.yml|.github/workflows/publish-release.yml) ;;
*) echo "Recovery cannot include product or documentation changes: $changed" >&2; exit 1 ;;
esac
done
gh api "repos/$GITHUB_REPOSITORY/releases?per_page=100" \
| jq -e --arg version "$RELEASE_VERSION" '[.[] | select(.tag_name == $version)] | length == 0'

- name: Download original immutable bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
artifact-ids: ${{ env.SOURCE_ARTIFACT }}
run-id: ${{ env.SOURCE_RUN }}
github-token: ${{ github.token }}
path: ${{ runner.temp }}/release-input
merge-multiple: true

- name: Verify original release checksums
shell: bash
run: |
set -euo pipefail
cd "$RUNNER_TEMP/release-input"
[[ "$(find . -maxdepth 1 -type f | wc -l)" -eq 5 ]]
[[ "$(sha256sum iron-age-furniture-0.4.0.114041.jar | cut -d ' ' -f 1)" == 721b1f9476c8d682672bc5dd19b88835be9d787aec51d6192af6153205bccb1e ]]
[[ "$(sha256sum iron-age-furniture-0.4.0.114041-sources.jar | cut -d ' ' -f 1)" == 58b6f3a57695e545d10a541263b54b66fece1de22759307531ecd592a1382ca4 ]]
[[ "$(sha256sum iron-age-furniture-0.4.0.114041-javadoc.jar | cut -d ' ' -f 1)" == 6ffe5c00ec15a00ef6c826371010d3ab7851e4156a6a4462cdbb8e20ba378f3a ]]
sha256sum --check SHA256SUMS

publish_maven:
name: Publish original Maven bundle
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out publication repair
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.sha }}

- name: Install pinned Java 25 Mavenizer runtime
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '25.0.3+9.0.LTS'

- name: Install pinned Java 8 toolchain
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '8.0.502+7'

- name: Install Java 17 for Gradle
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: temurin
java-version: '17.0.1+12'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6

- name: Download original immutable bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
artifact-ids: ${{ env.SOURCE_ARTIFACT }}
run-id: ${{ env.SOURCE_RUN }}
github-token: ${{ github.token }}
path: ${{ runner.temp }}/release-input
merge-multiple: true

- name: Publish only the prepared Maven artifacts
env:
MAVEN_UPLOAD_URL: ${{ secrets.MAVEN_UPLOAD_URL }}
MAVEN_UPLOAD_USERNAME: ${{ secrets.MAVEN_UPLOAD_USERNAME }}
MAVEN_UPLOAD_PASSWORD: ${{ secrets.MAVEN_UPLOAD_PASSWORD }}
shell: bash
run: |
set -euo pipefail
chmod +x ./gradlew
./gradlew publishMavenJavaPublicationToReleaseRepository \
-PpreparedReleaseDir="$RUNNER_TEMP/release-input" \
--no-daemon --stacktrace \
-Dorg.gradle.java.installations.paths="$JAVA_HOME_25_X64,$JAVA_HOME_8_X64,$JAVA_HOME" \
-Dorg.gradle.java.installations.auto-detect=false \
-Dorg.gradle.java.installations.auto-download=false

publish_curseforge:
name: Publish original CurseForge bundle
needs: publish_maven
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Download original immutable bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
artifact-ids: ${{ env.SOURCE_ARTIFACT }}
run-id: ${{ env.SOURCE_RUN }}
github-token: ${{ github.token }}
path: ${{ runner.temp }}/release-input
merge-multiple: true

- name: Verify bundle
run: cd "$RUNNER_TEMP/release-input" && sha256sum --check SHA256SUMS

- name: Publish Forge 1.14.4 release
uses: Kira-NT/mc-publish@52307b03863581dec6b652b83e597aec02ebb075 # v3.3.1
with:
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
curseforge-id: '341834'
curseforge-dependencies: |
biomes-o-plenty(optional)
immersive-engineering(optional)
minecraft-mineralogy(optional)
base-metals(optional)
mrcrayfish-furniture-mod(optional)
name: IronAgeFurniture 0.4.0.114041 for Minecraft 1.14.4 (Forge)
version: '0.4.0.114041'
version-type: release
changelog-file: ${{ runner.temp }}/release-input/CHANGELOG.md
loaders: forge
game-versions: '1.14.4'
game-version-filter: none
environment: client | server
java: Java 8
fail-mode: fail
files: |
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041.jar
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041-sources.jar
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041-javadoc.jar

publish_github:
name: Publish original GitHub bundle
needs: publish_curseforge
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
actions: read
contents: write
steps:
- name: Download original immutable bundle
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
artifact-ids: ${{ env.SOURCE_ARTIFACT }}
run-id: ${{ env.SOURCE_RUN }}
github-token: ${{ github.token }}
path: ${{ runner.temp }}/release-input
merge-multiple: true

- name: Verify bundle
run: cd "$RUNNER_TEMP/release-input" && sha256sum --check SHA256SUMS

- name: Publish GitHub Release last
uses: Kira-NT/mc-publish@52307b03863581dec6b652b83e597aec02ebb075 # v3.3.1
with:
github-token: ${{ github.token }}
github-tag: ${{ env.RELEASE_VERSION }}
github-commitish: ${{ env.RELEASE_SHA }}
github-draft: false
github-prerelease: false
name: IronAgeFurniture 0.4.0.114041 for Minecraft 1.14.4 (Forge)
version: '0.4.0.114041'
version-type: release
changelog-file: ${{ runner.temp }}/release-input/CHANGELOG.md
fail-mode: fail
files: |
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041.jar
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041-sources.jar
${{ runner.temp }}/release-input/iron-age-furniture-0.4.0.114041-javadoc.jar
${{ runner.temp }}/release-input/SHA256SUMS
32 changes: 32 additions & 0 deletions gradle/release/publishing.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
def expectedMavenGroup = buildSupport.expectedMavenGroup
def expectedMavenArtifact = buildSupport.expectedMavenArtifact
def expectedMavenCoordinate = buildSupport.expectedMavenCoordinate
def expectedReleaseFiles = buildSupport.expectedReleaseFiles
def preparedReleaseDir = buildSupport.preparedReleaseDir
def releaseJar = buildSupport.releaseJar

Expand Down Expand Up @@ -74,3 +75,34 @@ tasks.withType(PublishToMavenRepository).configureEach {
? tasks.named('verifyPreparedReleaseArtifacts')
: tasks.named('verifyReleaseArtifacts'))
}

tasks.register('verifyPreparedMavenPublication') {
group = 'verification'
description = 'Checks that Maven publishes only the three jars in the prepared release bundle.'
dependsOn tasks.named('verifyPreparedReleaseArtifacts')
dependsOn tasks.named('verifyMavenCoordinates')
doLast {
if (!preparedReleaseDir.isPresent()) {
throw new GradleException('preparedReleaseDir is required')
}
File prepared = file(preparedReleaseDir.get()).canonicalFile
def publication = publishing.publications.mavenJava
Map<String, String> actual = publication.artifacts.collectEntries { artifact ->
if (artifact.file.canonicalFile.parentFile != prepared || artifact.extension != 'jar') {
throw new GradleException('Maven publication must use the prepared jars without rebuilding them')
}
[(artifact.classifier ?: ''): artifact.file.name]
}
def names = expectedReleaseFiles.get()
Map<String, String> expected = ['': names[0], sources: names[1], javadoc: names[2]]
if (publication.artifacts.size() != 3 || actual != expected) {
throw new GradleException("Prepared Maven artifacts ${actual} do not match ${expected}")
}
}
}

if (preparedReleaseDir.isPresent()) {
tasks.withType(PublishToMavenRepository).configureEach {
dependsOn tasks.named('verifyPreparedMavenPublication')
}
}
Loading