name: Project Build run-name: "đŸ“Ļ Project Build #${{ github.run_number }}" on: push: branches: - dev - main concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: DEV_ENVIRONMENT: ${{ github.ref_name != 'main' }} jobs: info: name: đŸ–Ĩī¸ Project Info runs-on: ubuntu-latest if: ${{ github.repository_owner == 'TerraFirmaGreg-Team' }} outputs: project_version: ${{ steps.check.outputs.project_version }} project_name: ${{ steps.check.outputs.project_name }} project_full_name: ${{ steps.check.outputs.project_name }}-${{ steps.check.outputs.project_version }} changelog: ${{ steps.changelog.outputs.description }} mc_version: ${{ steps.check.outputs.minecraft_version }} loader_version: ${{ steps.check.outputs.loader_version }} loader_type: ${{ steps.check.outputs.loader_type }} release_type: ${{ steps.check.outputs.release_type }} diff: ${{ steps.read_diff.outputs.diff }} exists: ${{ steps.check.outputs.exists }} make_release: ${{ steps.check.outputs.make_release }} make_pr: ${{ steps.check.outputs.make_pr }} steps: - name: đŸ“Ļ Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: 🔍 Check pakku-lock.json id: check_pakku_lock shell: bash run: | if [ ! -f pakku-lock.json ]; then echo "❌ Could not find pakku-lock.json" && exit 1 else echo "âœ”ī¸ pakku-lock.json" fi - name: 🔍 Check pakku.json id: check_pakku shell: bash run: | if [ ! -f pakku.json ]; then echo "❌ Could not find pakku.json" && exit 1 else echo "âœ”ī¸ pakku.json" fi - name: 📈 Get latest tag id: latest_tag shell: bash run: | tag=$(git describe --tags --abbrev=0 origin/main) if [ -z "$tag" ]; then echo "❌ Latest tag not found" && exit 1 else echo "âœ”ī¸ Latest tag found: $tag" echo "tag=$tag" >> $GITHUB_OUTPUT fi - name: 🔍 Check pakku-lock.json in previous tag id: check_pakku_lock_prev shell: bash run: | if git cat-file -e ${{ steps.latest_tag.outputs.tag }}:./pakku-lock.json 2>/dev/null; then echo "âœ”ī¸ File pakku-lock.json found in previous tag" echo "file_found=true" >> $GITHUB_OUTPUT else echo "❌ File pakku-lock.json not found in previous tag" echo "file_found=false" >> $GITHUB_OUTPUT fi - name: 📁 Check and copy pakku-lock.json from previous tag id: check_copy_lock if: steps.check_pakku_lock_prev.outputs.file_found shell: bash run: | git show tags/${{ steps.latest_tag.outputs.tag }}:./pakku-lock.json > ./pakku-lock-prev.json if [ -s ./pakku-lock-prev.json ]; then echo "âœ”ī¸ File pakku-lock-prev.json created" else echo "❌ Error: File pakku-lock-prev.json is empty or not created" && exit 1 fi # - name: đŸ“Ļ Download pakku.jar # id: download_pakku # if: steps.check_pakku_lock_prev.outputs.file_found # shell: bash # run: | # curl https://github.com/juraj-hrivnak/pakku/releases/latest/download/pakku.jar -o pakku.jar -L -J # echo "âœ”ī¸ Downloaded pakku.jar " - name: 🔄 Run pakku diff id: pakku_diff if: steps.check_pakku_lock_prev.outputs.file_found shell: bash run: | java -jar pakku.jar diff -v --markdown PROJECTS_DIFF.md ./pakku-lock-prev.json ./pakku-lock.json if [ -f PROJECTS_DIFF.md ]; then echo "âœ”ī¸ Comparison completed" else echo "❌ Error: File PROJECTS_DIFF.md not created" && exit 1 fi - name: 📝 Read PROJECTS_DIFF.md to variable id: read_diff if: steps.check_pakku_lock_prev.outputs.file_found shell: bash run: | echo "📝 Reading PROJECTS_DIFF.md to variable..." { echo 'diff<> "$GITHUB_OUTPUT" echo "âœ”ī¸ Diff content read to variable" - name: 📊 Get Pakku Info id: pakku_info uses: ActionsTools/read-json-action@v1.0.5 with: file_path: "pakku.json" - name: 📊 Get Pakku-lock Info id: pakku_lock_info uses: ActionsTools/read-json-action@v1.0.5 with: file_path: "pakku-lock.json" - name: 📄 Changelog Dev Parser id: changelog_dev if: ${{ env.DEV_ENVIRONMENT == 'true' }} uses: coditory/changelog-parser@v1.0.2 with: path: CHANGELOG.md version: "unreleased" continue-on-error: true - name: 📄 Changelog Parser id: changelog uses: coditory/changelog-parser@v1.0.2 with: path: CHANGELOG.md continue-on-error: true - name: 🔍 Check if tag exists uses: mukunku/tag-exists-action@v1.7.0 id: check_tag with: tag: ${{ steps.changelog.outputs.version }} - name: 🔍 Check id: check shell: bash run: | MC_VERSIONS_JSON='${{ steps.pakku_lock_info.outputs.mc_versions }}' MINECRAFT_VERSION=$(echo "$MC_VERSIONS_JSON" | jq -r '.[0]' | tr -d '[]"') LOADERS_JSON='${{ steps.pakku_lock_info.outputs.loaders }}' LOADER_TYPE=$(echo "$LOADERS_JSON" | jq -r 'keys[0]') LOADER_VERSION=$(echo "$LOADERS_JSON" | jq -r ".[keys[0]]") echo "minecraft_version=$MINECRAFT_VERSION" >> $GITHUB_OUTPUT echo "loader_version=$LOADER_VERSION" >> $GITHUB_OUTPUT echo "loader_type=$LOADER_TYPE" >> $GITHUB_OUTPUT if ${{ env.DEV_ENVIRONMENT == 'true' }}; then echo "project_version=build_#${{ github.run_number }}" >> $GITHUB_OUTPUT else echo "project_version=${{ steps.changelog.outputs.version }}" >> $GITHUB_OUTPUT fi echo "project_name=${{ steps.pakku_info.outputs.name }}" >> $GITHUB_OUTPUT echo "release_type=${{ steps.pakku_info.outputs.release_type }}" >> $GITHUB_OUTPUT echo "exists=${{ steps.check_tag.outputs.exists }}" >> $GITHUB_OUTPUT echo "make_release=${{ steps.check_tag.outputs.exists == 'false' && env.DEV_ENVIRONMENT == 'false' }}" >> $GITHUB_OUTPUT echo "make_pr=${{ steps.check_tag.outputs.exists == 'false' && env.DEV_ENVIRONMENT == 'true' }}" >> $GITHUB_OUTPUT - name: 📄 Format diff id: format_diff if: ${{ steps.read_diff.outputs.diff != '' }} uses: roamingowl/template-output-with-eta@v2.2.0 with: template: | ```markdown ${{ steps.read_diff.outputs.diff }} ``` - name: 📝 Generate Github Summary uses: WcAServices/markdown-template-action@v1.1.1 with: template: | 📃 **Name**: ${{ steps.check.outputs.project_name }} 📃 **Release**: `${{ steps.check.outputs.project_version }}` 📃 **Release Type**: `${{ steps.check.outputs.release_type }}` 📃 **Game Version**: `${{ steps.check.outputs.minecraft_version }}` 📃 **Loader Type**: `${{ steps.check.outputs.loader_type }}` 📃 **Loader Version**: `${{ steps.check.outputs.loader_version }}` 📃 **Dev Environment**: `${{ env.DEV_ENVIRONMENT }}` 📃 **Tag Exists**: `${{ steps.check.outputs.exists }}` 📃 **Make Pull Request**: `${{ steps.check.outputs.make_pr }}` 📃 **Make Release**: `${{ steps.check.outputs.make_release }}` ${{ steps.changelog_dev.outputs.description }} ${{ steps.format_diff.outputs.text }} create-pr: name: 🤝 Create Release PR needs: [info] if: ${{ needs.info.outputs.make_pr == 'true' }} runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: 📄 Changelog Parser id: changelog uses: coditory/changelog-parser@v1.0.2 with: path: CHANGELOG.md continue-on-error: true - name: 🔍 Check existing PRs id: check_existing_pr uses: actions/github-script@v8.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: result-encoding: string script: | try { const { data: prs } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', head: `${context.repo.owner}:dev`, base: 'main' }); core.setOutput('exists', prs.length > 0); console.log(`â„šī¸ PR exists: ${prs.length > 0}`); } catch (error) { core.setFailed(`❌ PR check failed: ${error}`); core.setOutput('exists', true); } - name: 🔍 Check if milestone exists id: check_milestone uses: actions/github-script@v8.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: result-encoding: string script: | const version = '${{ steps.changelog.outputs.version }}'; try { const milestones = await github.rest.issues.listMilestones({ owner: context.repo.owner, repo: context.repo.repo, state: 'all' }); const milestoneExists = milestones.data.some(milestone => milestone.title === version); console.log(`Milestone "${version}" exists: ${milestoneExists}`); return milestoneExists; } catch (error) { console.log('Error checking milestones, assuming milestone does not exist'); return false; } - name: 🔍 Create Pull Request if tag not found if: ${{ steps.check_existing_pr.outputs.exists == 'false' }} uses: devops-infra/action-pull-request@v1.0.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} source_branch: dev target_branch: main milestone: ${{ steps.check_milestone.outputs.result == 'true' && steps.changelog.outputs.version || '' }} ignore_users: "dependabot" draft: true title: 'Release: ${{ steps.changelog.outputs.version }}' body: | **This is an automated Pull Request from the dev branch.** 📃 **Name**: ${{ needs.info.outputs.project_name }} 📃 **Release**: `${{ steps.changelog.outputs.version }}` 📃 **Release Type**: `${{ needs.info.outputs.release_type }}` 📃 **Loader**: `${{ needs.info.outputs.loader_type }}-${{ needs.info.outputs.loader_version }}` ${{ needs.info.outputs.changelog }} build-project: name: đŸ“Ļ Build Project needs: [info] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6.0.2 - name: 🔄 Replace strings shell: bash run: | set +e VERSION=${{ needs.info.outputs.project_version }} LOADER_VERSION=${{ needs.info.outputs.loader_version }} LOADER_TYPE=${{ needs.info.outputs.loader_type }} MINECRAFT_VERSION=${{ needs.info.outputs.mc_version }} sed -i -e "s/DEV/${VERSION}/g" pakku.json sed -i -e "s/DEV/${VERSION}/g" config/mod-director/modpack.json sed -i -e "s/DEV/${VERSION}/g" config/fancymenu/customization/gui_main_menu.txt sed -i -e "s/DEV/${VERSION}/g" .pakku/multimc-overrides/instance.cfg sed -i -e "s/DEV/${VERSION}/g" .pakku/server-overrides/server.properties sed -i -e "s/DEV/${VERSION}/g" .pakku/docker-overrides/docker-compose.yml sed -i -e "s/LOADER_VERSION/${LOADER_VERSION}/g" .pakku/multimc-overrides/mmc-pack.json sed -i -e "s/LOADER_VERSION/${LOADER_VERSION}/g" .pakku/server-overrides/forge-auto-install.txt sed -i -e "s/LOADER_VERSION/${LOADER_VERSION}/g" .pakku/docker-overrides/docker-compose.yml sed -i -e "s/LOADER_TYPE/${LOADER_TYPE}/g" .pakku/multimc-overrides/mmc-pack.json sed -i -e "s/LOADER_TYPE/${LOADER_TYPE}/g" .pakku/server-overrides/forge-auto-install.txt sed -i -e "s/LOADER_TYPE/${LOADER_TYPE}/g" .pakku/docker-overrides/docker-compose.yml sed -i -e "s/MINECRAFT_VERSION/${MINECRAFT_VERSION}/g" .pakku/multimc-overrides/mmc-pack.json sed -i -e "s/MINECRAFT_VERSION/${MINECRAFT_VERSION}/g" .pakku/server-overrides/forge-auto-install.txt sed -i -e "s/MINECRAFT_VERSION/${MINECRAFT_VERSION}/g" .pakku/docker-overrides/docker-compose.yml - name: 📝 Cache Pakku uses: actions/cache@v5.0.3 id: cache with: path: build/.cache key: pakku-cache-${{ hashFiles('pakku-lock.json') }} restore-keys: pakku-cache- - name: đŸ“Ļ Export Modpack run: | # curl https://github.com/juraj-hrivnak/pakku/releases/latest/download/pakku.jar -o pakku.jar -L -J java -jar pakku.jar fetch java -jar pakku.jar export mkdir -p .pakku/multimc-overrides/flame mkdir -p .pakku/multimc-overrides/mods - name: 📁 Preparing the artifact CurseForge run: | cd ./build/curseforge/ mv *.zip $(basename -s .zip *.zip)-curseforge.zip - name: 🚀 Upload artifact CurseForge uses: actions/upload-artifact@v6.0.0 with: name: ${{ needs.info.outputs.project_full_name }}-curseforge path: ./build/curseforge/${{ needs.info.outputs.project_full_name }}-curseforge.zip if-no-files-found: error - name: 📁 Preparing the artifact Modrinth run: | cd ./build/modrinth/ mv *.mrpack $(basename -s .mrpack *.mrpack)-modrinth.mrpack - name: 🚀 Upload artifact Modrinth uses: actions/upload-artifact@v6.0.0 with: name: ${{ needs.info.outputs.project_full_name }}-modrinth path: ./build/modrinth/${{ needs.info.outputs.project_full_name }}-modrinth.mrpack if-no-files-found: warn - name: 📁 Preparing the artifact MultiMC run: | mkdir -p .pakku/multimc-overrides/flame mkdir -p .pakku/multimc-overrides/mods mv -vf .pakku/multimc-overrides ./build/multimc # Delete mods with export: false from ./mods directory echo "Checking for mods with export: false..." if [ -f "./pakku.json" ]; then # Extract mod slugs with export: false and delete corresponding jar files for mod in $(jq -r 'to_entries[] | select(.value.export == false) | .key' ./pakku.json); do echo "Looking for mod: $mod" find ./mods -name "${mod}*.jar" -type f -delete 2>/dev/null && echo "Deleted $mod jar files" || echo "No $mod jar files found" done else echo "pakku.json not found, skipping export:false mod removal" fi cp -rf ./build/.cache/curseforge/overrides ./build/multimc/.minecraft cp -vf ./build/.cache/curseforge/manifest.json ./build/multimc/flame/manifest.json cp -vf ./config/fancymenu/assets/icons/icon128x128.png ./build/multimc/.minecraft/icon.png cp -rf ./mods ./build/multimc/.minecraft/ cd ./build/multimc/ zip -r ${{ needs.info.outputs.project_full_name }}-multimc.zip icon.png mmc-pack.json instance.cfg .minecraft/ flame/ - name: 🚀 Upload artifact MultiMC uses: actions/upload-artifact@v6.0.0 with: name: ${{ needs.info.outputs.project_full_name }}-multimc path: ./build/multimc/${{ needs.info.outputs.project_full_name }}-multimc.zip if-no-files-found: error - name: 📁 Preparing the artifact Server run: | cd ./build/serverpack/ mv *.zip $(basename -s .zip *.zip)-serverpack.zip - name: 🚀 Upload artifact Server uses: actions/upload-artifact@v6.0.0 with: name: ${{ needs.info.outputs.project_full_name }}-serverpack path: ./build/serverpack/${{ needs.info.outputs.project_full_name }}-serverpack.zip if-no-files-found: error - name: 📁 Preparing the artifact Docker run: | mv -vf .pakku/docker-overrides ./build/docker cd ./build/docker/ mv docker-compose.yml "${{ needs.info.outputs.project_full_name }}-docker.yml" - name: 🚀 Upload Docker Artifact uses: actions/upload-artifact@v6.0.0 with: name: ${{ needs.info.outputs.project_full_name }}-docker path: ./build/docker/${{ needs.info.outputs.project_full_name }}-docker.yml if-no-files-found: error release-github: name: 🚀 Release to GitHub needs: [info, build-project] runs-on: ubuntu-latest if: ${{ needs.info.outputs.make_release == 'true' }} outputs: url: ${{ steps.release.outputs.url }} steps: - name: đŸ“Ļ Checkout uses: actions/checkout@v6.0.2 - name: đŸ“Ļ Download artifact uses: actions/download-artifact@v7.0.0 with: merge-multiple: true - name: 🔍 Check if artifact exist id: check_artifact shell: bash run: | if [ ! -f ${{ needs.info.outputs.project_full_name }}-curseforge.zip ]; then echo '::error::No value found for artifact `curseforge.zip`.' && exit 1 fi # if [ ! -f ${{ needs.info.outputs.project_full_name }}-modrinth.mrpack ]; then # echo '::error::No value found for artifact `modrinth.mrpack`.' && exit 1 # fi if [ ! -f ${{ needs.info.outputs.project_full_name }}-serverpack.zip ]; then echo '::error::No value found for artifact `serverpack.zip`.' && exit 1 fi if [ ! -f ${{ needs.info.outputs.project_full_name }}-multimc.zip ]; then echo '::error::No value found for artifact `multimc.zip`.' && exit 1 fi if [ ! -f ${{ needs.info.outputs.project_full_name }}-docker.yml ]; then echo '::error::No value found for artifact `docker.yml`.' && exit 1 fi echo "âœ”ī¸ All artifacts found" - name: 📄 Format diff id: format_diff if: ${{ needs.info.outputs.diff != '' }} uses: roamingowl/template-output-with-eta@v2.2.0 with: template: | ```markdown ${{ needs.info.outputs.diff }} ``` - name: 🚀 Create release id: release uses: softprops/action-gh-release@v2.5.0 with: name: ${{ needs.info.outputs.project_version }} tag_name: ${{ needs.info.outputs.project_version }} target_commitish: ${{ github.ref_name }} body: | ${{ needs.info.outputs.changelog }} ${{ steps.format_diff.outputs.text }} files: | ${{ needs.info.outputs.project_full_name }}-curseforge.zip ${{ needs.info.outputs.project_full_name }}-modrinth.mrpack ${{ needs.info.outputs.project_full_name }}-serverpack.zip ${{ needs.info.outputs.project_full_name }}-multimc.zip ${{ needs.info.outputs.project_full_name }}-docker.yml prerelease: ${{ needs.info.outputs.release_type != 'release' }} generate_release_notes: true token: ${{ secrets.GITHUB_TOKEN }} release-curseforge: name: 🚀 Release to CurseForge needs: [info, build-project, release-github] runs-on: ubuntu-latest if: ${{ needs.info.outputs.make_release == 'true' }} outputs: id: ${{ steps.release.outputs.id }} steps: - name: 🔒 Check if CURSEFORGE_TOKEN exist shell: bash run: | if [ "${{ secrets.CURSEFORGE_TOKEN }}" == '' ]; then echo '::error::No value found for secret key `CURSEFORGE_TOKEN`. See https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository' && exit 1 fi - name: đŸ“Ļ Download artifact uses: actions/download-artifact@v7.0.0 with: merge-multiple: true - name: 🔍 Check if artifact exist id: check_artifact shell: bash run: | if [ ! -f ${{ needs.info.outputs.project_full_name }}-curseforge.zip ]; then echo '::error::No value found for artifact `curseforge.zip`.' && exit 1 fi if [ ! -f ${{ needs.info.outputs.project_full_name }}-serverpack.zip ]; then echo '::error::No value found for artifact `serverpack.zip`.' && exit 1 fi echo "âœ”ī¸ All artifacts found" - name: 🚀 Upload Curseforge id: release uses: Xikaro/upload-curseforge-modpack-action@1.1.1 with: api-token: ${{ secrets.CURSEFORGE_TOKEN }} project-id: ${{ vars.CURSEFORGE_ID }} display-name: ${{ needs.info.outputs.project_full_name }} modpack-path: ${{ needs.info.outputs.project_full_name }}-curseforge.zip server-display-name: ${{ needs.info.outputs.project_full_name }}-serverpack modpack-server-path: ${{ needs.info.outputs.project_full_name }}-serverpack.zip changelog: | ${{ needs.info.outputs.changelog }} ${{ needs.info.outputs.diff }} changelog-format: markdown game-version: ${{ needs.info.outputs.mc_version }} release-type: ${{ needs.info.outputs.release_type }} release-modrinth: name: 🚀 Release to Modrinth needs: [info, build-project, release-github] runs-on: ubuntu-latest if: ${{ needs.info.outputs.make_release == 'true' }} outputs: id: ${{ steps.release.outputs.id }} steps: - name: 🔒 Check if MODRINTH_API_TOKEN exist shell: bash run: | if [ "${{ secrets.MODRINTH_TOKEN }}" == '' ]; then echo '::error::No value found for secret key `MODRINTH_TOKEN`. See https://docs.github.com/en/ actionssecurity-guides/ encrypted-secrets#creating-encrypted-secrets-for-a-repository' && exit 1 fi - name: đŸ“Ļ Download artifact uses: actions/download-artifact@v7.0.0 with: merge-multiple: true - name: 🔍 Check if artifact exist id: check_artifact shell: bash run: | if [ ! -f ${{ needs.info.outputs.project_full_name }}-modrinth.mrpack ]; then echo '::error::No value found for artifact `modrinth.mrpack`.' && exit 1 fi echo "âœ”ī¸ All artifacts found" # - name: 🚀 Upload Modrinth # id: release # uses: Xikaro/upload-curseforge-modpack-action@1.1.1 # with: # api-token: ${{ secrets.MODRINTH_TOKEN }} # project-id: ${{ vars.MODRINTH_ID }} # modpack-path: ${{ needs.info.outputs.project_full_name }}-modrinth.mrpack # changelog: ${{ needs.info.outputs.changelog }} # changelog-format: markdown # game-version: ${{ needs.info.outputs.mc_version }} # display-name: ${{ needs.info.outputs.project_full_name }} # release-type: ${{ needs.info.outputs.release_type }} discord-message: name: 📱 Discord Message needs: [info, release-github, release-curseforge] runs-on: ubuntu-latest steps: - name: âœ‚ī¸ Truncate Changelog id: truncated uses: cisox/read-more-action@v1.0.2 with: text: '${{ needs.info.outputs.changelog }}' max_chars: '1450' - name: 📨 Send Discord message uses: hugoalh/send-discord-webhook-ghaction@v7.0.5 with: key: ${{ secrets.DISCORD_RELEASES }} username: "TerraFirmaGreg" avatar_url: "https://raw.githubusercontent.com/TerraFirmaGreg-Team/.github/main/branding/logo.png" content_links_no_embed: .+ content: | **Release**: `${{ needs.info.outputs.project_version }}` **Release Type**: `${{ needs.info.outputs.release_type }}` **Game Version**: `${{ needs.info.outputs.mc_version }}` [CurseForge](https://www.curseforge.com/minecraft/modpacks/terrafirmagreg-modern/files/${{ needs.release-curseforge.outputs.id }}) â€ĸ [GitHub](${{ needs.release-github.outputs.url }}) â€ĸ [Issues](https://github.com/${{ github.repository }}/issues) ```markdown ${{ steps.truncated.outputs.text }} - ...``` ** [Read more...](${{ needs.release-github.outputs.url }}) **