Release Build #37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Build | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| publish_discord: | |
| description: "Send Discord notification" | |
| required: true | |
| type: boolean | |
| default: true | |
| publish_github: | |
| description: "Publish to GitHub Releases" | |
| required: true | |
| type: boolean | |
| default: true | |
| publish_curseforge: | |
| description: "Publish to CurseForge" | |
| required: true | |
| type: boolean | |
| default: true | |
| publish_modrinth: | |
| description: "Publish to Modrinth" | |
| required: true | |
| type: boolean | |
| default: true | |
| confirm: | |
| description: "Type RELEASE to confirm" | |
| required: true | |
| type: string | |
| concurrency: | |
| group: release-${{ github.ref_name || github.run_id }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate-release: | |
| name: Validate Release Target | |
| runs-on: ubuntu-latest | |
| outputs: | |
| is_version_branch: ${{ steps.branch_check.outputs.is_version_branch }} | |
| target_branch: ${{ steps.branch_check.outputs.target_branch }} | |
| publish_discord: ${{ steps.branch_check.outputs.publish_discord }} | |
| publish_github: ${{ steps.branch_check.outputs.publish_github }} | |
| publish_curseforge: ${{ steps.branch_check.outputs.publish_curseforge }} | |
| publish_modrinth: ${{ steps.branch_check.outputs.publish_modrinth }} | |
| steps: | |
| - name: Resolve trigger inputs | |
| id: branch_check | |
| shell: bash | |
| env: | |
| TARGET_BRANCH: ${{ github.ref_name }} | |
| INPUT_PUBLISH_DISCORD: ${{ inputs.publish_discord }} | |
| INPUT_PUBLISH_GITHUB: ${{ inputs.publish_github }} | |
| INPUT_PUBLISH_CURSEFORGE: ${{ inputs.publish_curseforge }} | |
| INPUT_PUBLISH_MODRINTH: ${{ inputs.publish_modrinth }} | |
| INPUT_CONFIRM: ${{ inputs.confirm }} | |
| run: | | |
| target="$TARGET_BRANCH" | |
| publish_discord="$INPUT_PUBLISH_DISCORD" | |
| publish_github="$INPUT_PUBLISH_GITHUB" | |
| publish_curseforge="$INPUT_PUBLISH_CURSEFORGE" | |
| publish_modrinth="$INPUT_PUBLISH_MODRINTH" | |
| if [[ "$INPUT_CONFIRM" != "RELEASE" ]]; then | |
| echo "Manual confirmation failed. Type RELEASE in the confirm input." | |
| exit 1 | |
| fi | |
| # Accept branch names like 26.1, 1.21.1, 1.20.2, etc. | |
| if [[ "$target" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then | |
| echo "is_version_branch=true" >> "$GITHUB_OUTPUT" | |
| echo "Release target branch '$target' accepted." | |
| else | |
| echo "is_version_branch=false" >> "$GITHUB_OUTPUT" | |
| echo "Release target branch '$target' is not a version branch. Workflow will skip." | |
| fi | |
| echo "target_branch=$target" >> "$GITHUB_OUTPUT" | |
| echo "publish_discord=$publish_discord" >> "$GITHUB_OUTPUT" | |
| echo "publish_github=$publish_github" >> "$GITHUB_OUTPUT" | |
| echo "publish_curseforge=$publish_curseforge" >> "$GITHUB_OUTPUT" | |
| echo "publish_modrinth=$publish_modrinth" >> "$GITHUB_OUTPUT" | |
| build: | |
| name: Build | |
| needs: validate-release | |
| if: needs.validate-release.outputs.is_version_branch == 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| minecraft_version: ${{ steps.meta.outputs.minecraft_version }} | |
| mod_version: ${{ steps.meta.outputs.mod_version }} | |
| mod_name: ${{ steps.meta.outputs.mod_name }} | |
| release_tag: ${{ steps.meta.outputs.release_tag }} | |
| changelog: ${{ steps.meta.outputs.changelog }} | |
| java_version: ${{ steps.meta.outputs.java_version }} | |
| loader: ${{ steps.meta.outputs.loader }} | |
| steps: | |
| - name: Checkout target branch | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.ref }} | |
| - name: Read project metadata | |
| id: meta | |
| shell: bash | |
| run: | | |
| minecraft_version="$(awk -F= '/^minecraft_version=/{print $2}' gradle.properties | tr -d '\r')" | |
| mod_version="$(awk -F= '/^mod_version=/{print $2}' gradle.properties | tr -d '\r')" | |
| mod_name="$(awk -F= '/^mod_name=/{print $2}' gradle.properties | tr -d '\r')" | |
| release_tag="${minecraft_version}-${mod_version}" | |
| changelog_file="CHANGELOGS/${release_tag}.md" | |
| java_version="$(grep -oE 'JavaLanguageVersion\.of\([0-9]+\)' build.gradle.kts | head -n1 | grep -oE '[0-9]+' || true)" | |
| if [[ -z "$java_version" ]]; then | |
| java_version="21" | |
| fi | |
| if [[ ! -f "$changelog_file" ]]; then | |
| echo "Missing changelog file: $changelog_file" | |
| exit 1 | |
| fi | |
| if grep -q 'id("net.neoforged.moddev")' build.gradle.kts; then | |
| loader="neoforge" | |
| elif grep -q 'id("net.minecraftforge.gradle")' build.gradle.kts; then | |
| loader="forge" | |
| else | |
| loader="neoforge" | |
| fi | |
| echo "minecraft_version=$minecraft_version" >> "$GITHUB_OUTPUT" | |
| echo "mod_version=$mod_version" >> "$GITHUB_OUTPUT" | |
| echo "mod_name=$mod_name" >> "$GITHUB_OUTPUT" | |
| echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" | |
| echo "java_version=$java_version" >> "$GITHUB_OUTPUT" | |
| echo "loader=$loader" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "changelog<<EOF" | |
| cat "$changelog_file" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Set up JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: ${{ steps.meta.outputs.java_version }} | |
| - name: Set up Gradle | |
| uses: gradle/actions/setup-gradle@v3 | |
| - name: Ensure gradlew is executable | |
| run: chmod +x ./gradlew | |
| - name: Build | |
| shell: bash | |
| run: | | |
| exclude_args=() | |
| if ./gradlew tasks --all --no-daemon --console=plain | grep -qE '^copyJar\s'; then | |
| exclude_args=(-x copyJar) | |
| fi | |
| ./gradlew build "${exclude_args[@]}" --no-daemon --console=plain | |
| env: | |
| CI: true | |
| BUILD_VERSION: ${{ steps.meta.outputs.release_tag }} | |
| CHANGELOG: ${{ steps.meta.outputs.changelog }} | |
| PUBLISH: true | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-build | |
| path: build/libs/*.jar | |
| publish-github: | |
| name: Publish to GitHub Releases | |
| needs: [validate-release, build] | |
| if: needs.build.result == 'success' && needs.validate-release.outputs.publish_github == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| outputs: | |
| release_url: ${{ steps.github_release.outputs.url }} | |
| steps: | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-build | |
| path: build/libs | |
| - name: Publish to GitHub Releases | |
| id: github_release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| tag_name: ${{ needs.build.outputs.release_tag }} | |
| target_commitish: ${{ github.sha }} | |
| name: ${{ needs.build.outputs.mod_name }} - ${{ needs.build.outputs.release_tag }} | |
| body: ${{ needs.build.outputs.changelog }} | |
| files: build/libs/*.jar | |
| fail_on_unmatched_files: true | |
| publish-curseforge: | |
| name: Publish to CurseForge | |
| needs: [validate-release, build] | |
| if: needs.build.result == 'success' && needs.validate-release.outputs.publish_curseforge == 'true' && vars.CURSEFORGE_PROJECT_ID != '' | |
| runs-on: ubuntu-latest | |
| env: | |
| CURSEFORGE_TOKEN: ${{ secrets.TOKEN_CURSEFORGE }} | |
| outputs: | |
| release_url: ${{ steps.curseforge_publish.outputs.curseforge-url }} | |
| steps: | |
| - name: Validate CurseForge config | |
| id: curseforge_config | |
| shell: bash | |
| run: | | |
| if [[ -z "$CURSEFORGE_TOKEN" ]]; then | |
| echo "CurseForge token is not configured. Skipping publish." | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download artifacts | |
| if: steps.curseforge_config.outputs.enabled == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-build | |
| path: build/libs | |
| - name: Publish to CurseForge | |
| if: steps.curseforge_config.outputs.enabled == 'true' | |
| id: curseforge_publish | |
| uses: Kira-NT/mc-publish@v3.3.0 | |
| with: | |
| name: ${{ needs.build.outputs.mod_name }} - ${{ needs.build.outputs.release_tag }} | |
| version: ${{ needs.build.outputs.release_tag }} | |
| files: build/libs/*.jar | |
| changelog: ${{ needs.build.outputs.changelog }} | |
| loaders: ${{ needs.build.outputs.loader }} | |
| game-versions: ${{ needs.build.outputs.minecraft_version }} | |
| java: ${{ needs.build.outputs.java_version }} | |
| curseforge-id: ${{ vars.CURSEFORGE_PROJECT_ID }} | |
| curseforge-token: ${{ env.CURSEFORGE_TOKEN }} | |
| publish-modrinth: | |
| name: Publish to Modrinth | |
| needs: [validate-release, build] | |
| if: needs.build.result == 'success' && needs.validate-release.outputs.publish_modrinth == 'true' && vars.MODRINTH_PROJECT_ID != '' | |
| runs-on: ubuntu-latest | |
| env: | |
| MODRINTH_TOKEN: ${{ secrets.TOKEN_MODRINTH }} | |
| outputs: | |
| release_url: ${{ steps.modrinth_publish.outputs.modrinth-url }} | |
| steps: | |
| - name: Validate Modrinth config | |
| id: modrinth_config | |
| shell: bash | |
| run: | | |
| if [[ -z "$MODRINTH_TOKEN" ]]; then | |
| echo "Modrinth token is not configured. Skipping publish." | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download artifacts | |
| if: steps.modrinth_config.outputs.enabled == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-build | |
| path: build/libs | |
| - name: Publish to Modrinth | |
| if: steps.modrinth_config.outputs.enabled == 'true' | |
| id: modrinth_publish | |
| uses: Kira-NT/mc-publish@v3.3.0 | |
| with: | |
| name: ${{ needs.build.outputs.mod_name }} - ${{ needs.build.outputs.release_tag }} | |
| version: ${{ needs.build.outputs.release_tag }} | |
| files: build/libs/*.jar | |
| changelog: ${{ needs.build.outputs.changelog }} | |
| loaders: ${{ needs.build.outputs.loader }} | |
| game-versions: ${{ needs.build.outputs.minecraft_version }} | |
| java: ${{ needs.build.outputs.java_version }} | |
| modrinth-id: ${{ vars.MODRINTH_PROJECT_ID }} | |
| modrinth-token: ${{ env.MODRINTH_TOKEN }} | |
| notify-discord: | |
| name: Notify Discord | |
| needs: [validate-release, build, publish-github, publish-curseforge, publish-modrinth] | |
| if: always() && needs.validate-release.outputs.is_version_branch == 'true' && needs.validate-release.outputs.publish_discord == 'true' && needs.build.result == 'success' | |
| runs-on: ubuntu-latest | |
| env: | |
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} | |
| DISCORD_ROLE_ID: ${{ vars.DISCORD_RELEASE_ROLE_ID }} | |
| TARGET_BRANCH: ${{ needs.validate-release.outputs.target_branch }} | |
| MINECRAFT_VERSION: ${{ needs.build.outputs.minecraft_version }} | |
| MOD_VERSION: ${{ needs.build.outputs.mod_version }} | |
| RELEASE_TAG: ${{ needs.build.outputs.release_tag }} | |
| MOD_NAME: ${{ needs.build.outputs.mod_name }} | |
| CHANGELOG: ${{ needs.build.outputs.changelog }} | |
| BUILD_RESULT: ${{ needs.build.result }} | |
| GITHUB_RESULT: ${{ needs.publish-github.result }} | |
| GITHUB_URL: ${{ needs.publish-github.outputs.release_url }} | |
| CURSEFORGE_RESULT: ${{ needs.publish-curseforge.result }} | |
| CURSEFORGE_URL: ${{ needs.publish-curseforge.outputs.release_url }} | |
| MODRINTH_RESULT: ${{ needs.publish-modrinth.result }} | |
| MODRINTH_URL: ${{ needs.publish-modrinth.outputs.release_url }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| ACTOR: ${{ github.actor }} | |
| REPOSITORY: ${{ github.repository }} | |
| steps: | |
| - name: Validate Discord config | |
| id: discord_config | |
| shell: bash | |
| run: | | |
| if [[ -z "$DISCORD_WEBHOOK_URL" ]]; then | |
| echo "Discord webhook is not configured. Skipping notification." | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Send Discord notification | |
| if: steps.discord_config.outputs.enabled == 'true' | |
| shell: bash | |
| run: | | |
| payload=$(python3 - <<'PY' | |
| import json | |
| import os | |
| def link_line(label, url, result): | |
| if url: | |
| return f"{label}: {url}" | |
| if result == "skipped": | |
| return f"{label}: Skipped" | |
| if result == "success": | |
| return f"{label}: Published" | |
| if result == "failure": | |
| return f"{label}: Failed" | |
| if result == "cancelled": | |
| return f"{label}: Cancelled" | |
| return f"{label}: {result}" | |
| role_id = os.environ.get("DISCORD_ROLE_ID", "").strip() | |
| greeting = f"Hey <@&{role_id}>" if role_id else "Hey" | |
| release_name = f"LogisticsNetwork-{os.environ['MINECRAFT_VERSION']}-{os.environ['MOD_VERSION']}" | |
| lines = [ | |
| greeting, | |
| "", | |
| f"New release: {release_name}", | |
| "## Changelog", | |
| "", | |
| ] | |
| changelog = os.environ.get("CHANGELOG", "").strip() | |
| if changelog: | |
| lines.append(changelog) | |
| else: | |
| lines.append("No changelog provided.") | |
| lines.extend([ | |
| "", | |
| link_line("Curseforge", os.environ.get("CURSEFORGE_URL", "").strip(), os.environ.get("CURSEFORGE_RESULT", "").strip()), | |
| link_line("Modrinth", os.environ.get("MODRINTH_URL", "").strip(), os.environ.get("MODRINTH_RESULT", "").strip()), | |
| link_line("Github", os.environ.get("GITHUB_URL", "").strip(), os.environ.get("GITHUB_RESULT", "").strip()), | |
| "", | |
| "Thank you all for downloading,", | |
| "AlmanaX21", | |
| ]) | |
| content = "\n".join(lines) | |
| if len(content) > 1900: | |
| static_lines = [ | |
| greeting, | |
| "", | |
| f"New release: {release_name}", | |
| "## Changelog", | |
| "", | |
| ] | |
| footer_lines = [ | |
| "", | |
| link_line("Curseforge", os.environ.get("CURSEFORGE_URL", "").strip(), os.environ.get("CURSEFORGE_RESULT", "").strip()), | |
| link_line("Modrinth", os.environ.get("MODRINTH_URL", "").strip(), os.environ.get("MODRINTH_RESULT", "").strip()), | |
| link_line("Github", os.environ.get("GITHUB_URL", "").strip(), os.environ.get("GITHUB_RESULT", "").strip()), | |
| "", | |
| "Thank you all for downloading,", | |
| "AlmanaX21", | |
| ] | |
| static_prefix = "\n".join(static_lines) | |
| static_suffix = "\n".join(footer_lines) | |
| available = 1900 - len(static_prefix) - len(static_suffix) - len("\n...\n") | |
| trimmed = changelog[:max(0, available)].rstrip() | |
| content = "\n".join(static_lines + [trimmed, "..."] + footer_lines) | |
| payload = {"content": content} | |
| if role_id: | |
| payload["allowed_mentions"] = {"parse": [], "roles": [role_id]} | |
| print(json.dumps(payload)) | |
| PY | |
| ) | |
| curl \ | |
| --fail \ | |
| --silent \ | |
| --show-error \ | |
| -H "Content-Type: application/json" \ | |
| -d "$payload" \ | |
| "$DISCORD_WEBHOOK_URL" |