From de98b191cfacb553dd1f44d721e2952e85e09613 Mon Sep 17 00:00:00 2001 From: Alexander01998 Date: Sat, 11 Jan 2025 23:46:26 +0100 Subject: [PATCH 1/3] Use 'leadingSpacesToTabs' instead of 'indentWithTabs' in build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f4dd0e8..8188682 100644 --- a/build.gradle +++ b/build.gradle @@ -123,7 +123,7 @@ spotless { lineEndings("WINDOWS") java { removeUnusedImports() - indentWithTabs() + leadingSpacesToTabs() trimTrailingWhitespace() eclipse().configFile(file("codestyle/formatter.xml")) } From 64dc4db61efa3bf286afe32cbdecf0cbdea4d35a Mon Sep 17 00:00:00 2001 From: Alexander01998 Date: Sun, 12 Jan 2025 21:47:20 +0100 Subject: [PATCH 2/3] Update GHA workflows --- .github/workflows/auto_snapshot_update.yml | 36 ++-- .github/workflows/dependency_graph.yml | 3 +- .github/workflows/gradle.yml | 12 +- .github/workflows/publish.yml | 208 ++++++++++----------- .github/workflows/stale.yml | 56 +++--- 5 files changed, 165 insertions(+), 150 deletions(-) diff --git a/.github/workflows/auto_snapshot_update.yml b/.github/workflows/auto_snapshot_update.yml index 4206daf..2d8a99c 100644 --- a/.github/workflows/auto_snapshot_update.yml +++ b/.github/workflows/auto_snapshot_update.yml @@ -78,23 +78,24 @@ jobs: git checkout -b $BRANCH_NAME echo "Created and checked out new branch: $BRANCH_NAME" fi - shell: bash - name: Run migrateMappings task run: | ./gradlew migrateMappings --mappings ${{ github.event.inputs.yarn_mappings }} - shell: bash - name: Replace src/main/java with remapped files run: | rm -rf ./src/main/java mv ./remappedSrc ./src/main/java - shell: bash - name: Update version constants run: | - python scripts/update_version_constants.py "${{ github.event.inputs.mc_version }}" "${{ github.event.inputs.yarn_mappings }}" "${{ github.event.inputs.fabric_loader }}" "${{ github.event.inputs.fapi_version }}" "${{ github.event.inputs.cf_game_version }}" - shell: bash + python scripts/update_version_constants.py \ + "${{ github.event.inputs.mc_version }}" \ + "${{ github.event.inputs.yarn_mappings }}" \ + "${{ github.event.inputs.fabric_loader }}" \ + "${{ github.event.inputs.fapi_version }}" \ + "${{ github.event.inputs.cf_game_version }}" # To fix any style issues that the migration scripts might cause - name: Run spotlessApply task @@ -107,13 +108,22 @@ jobs: git add . git commit -m "[Wurst-Bot] Update to ${{ github.event.inputs.mc_version }}" git push --set-upstream origin ${{ github.event.inputs.mc_version }} - shell: bash - # For some reason the commit above doesn't automatically trigger the CI - # workflow, so we need to explicitly start it here. - name: Trigger CI on the new branch - env: - GH_TOKEN: ${{ github.token }} - run: | - gh workflow run gradle.yml --ref ${{ github.event.inputs.mc_version }} - shell: bash + id: ci_dispatch + uses: codex-/return-dispatch@v2 + with: + token: ${{ github.token }} + owner: Wurst-Imperium + repo: Mo-Glass + ref: ${{ github.event.inputs.mc_version }} + workflow: gradle.yml + + - name: Wait for CI to finish (run ${{ steps.ci_dispatch.outputs.run_id }}) + uses: codex-/await-remote-run@v1 + with: + token: ${{ github.token }} + owner: Wurst-Imperium + repo: Mo-Glass + run_id: ${{ steps.ci_dispatch.outputs.run_id }} + run_timeout_seconds: 600 # 10 minutes diff --git a/.github/workflows/dependency_graph.yml b/.github/workflows/dependency_graph.yml index 4d2765b..74a85a2 100644 --- a/.github/workflows/dependency_graph.yml +++ b/.github/workflows/dependency_graph.yml @@ -13,10 +13,11 @@ on: workflow_dispatch: permissions: + # Needed by the dependency-submission action. contents: write jobs: - dependency-submission: + dependency_graph: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 46e1b3d..44dde84 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -20,6 +20,10 @@ on: - "gradle**" - "*.gradle" workflow_dispatch: + inputs: + distinct_id: + description: "Automatically set by the return-dispatch action (leave blank if running manually)" + required: false jobs: build: @@ -29,6 +33,9 @@ jobs: IMGUR_CLIENT_ID: ${{ secrets.IMGUR_CLIENT_ID }} steps: + - name: Echo distinct ID ${{ github.event.inputs.distinct_id }} + run: echo ${{ github.event.inputs.distinct_id }} + - name: Checkout repository uses: actions/checkout@v4 @@ -85,7 +92,6 @@ jobs: - name: Add VirusTotal links to build summary if: ${{ env.VIRUSTOTAL_API_KEY && steps.virustotal.outputs.analysis }} - shell: bash run: | echo "
" >> $GITHUB_STEP_SUMMARY echo "🛡️ VirusTotal Scans" >> $GITHUB_STEP_SUMMARY @@ -122,21 +128,17 @@ jobs: echo "
" >> $GITHUB_STEP_SUMMARY echo "📸 Test Screenshots" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - for img in run/screenshots/*.png; do if [ -f "$img" ]; then filename=$(basename "$img") name_without_ext="${filename%.*}" - # Upload to Imgur response=$(curl -s -X POST \ -H "Authorization: Client-ID $IMGUR_CLIENT_ID" \ -F "image=@$img" \ https://api.imgur.com/3/image) - # Extract the URL from the response url=$(echo $response | grep -o '"link":"[^"]*"' | cut -d'"' -f4) - if [ ! -z "$url" ]; then # Convert underscores to spaces and capitalize first letter of each word title=$(echo "$name_without_ext" | tr '_' ' ' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1') diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9db6a7d..9cb634f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -54,107 +54,107 @@ jobs: MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} steps: - - name: Echo distinct ID ${{ github.event.inputs.distinct_id }} - run: echo ${{ github.event.inputs.distinct_id }} - - - name: Checkout repository - uses: actions/checkout@v4 - with: - # Include all tags in case the new tag already exists. - fetch-tags: true - - - name: Set up Java 21 - uses: actions/setup-java@v4 - with: - java-version: "21" - distribution: "microsoft" - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Build - run: ./gradlew build --stacktrace --warning-mode=fail - - - name: Create and push tag - run: | - MOD_VERSION=$(grep "^mod_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') - git config --global user.name "Wurst-Bot" - git config --global user.email "contact.wurstimperium@gmail.com" - git tag "v$MOD_VERSION" - git push origin "v$MOD_VERSION" - - - name: Close milestone - if: ${{ inputs.close_milestone }} - run: ./gradlew closeMilestone --stacktrace - - - name: Upload backups - if: ${{ inputs.upload_backups }} - run: ./gradlew uploadBackups --stacktrace - - - name: Publish to GitHub - if: ${{ inputs.publish_github }} - env: - GITHUB_TOKEN: ${{ secrets.MCX_PUBLISH_TOKEN }} - run: ./gradlew github --stacktrace - - - name: Publish to CurseForge - if: ${{ inputs.publish_curseforge }} - run: ./gradlew publishCurseforge --stacktrace - - - name: Get CurseForge file ID - id: cf_file_id - if: ${{ inputs.publish_curseforge }} - run: | - file_id=$(./gradlew getCurseforgeId -x publishCurseforge | grep -o 'CURSEFORGE_FILE_ID=[0-9]*' | grep -o '[0-9]*') - echo "file_id=$file_id" >> "$GITHUB_OUTPUT" - echo "CurseForge file ID: \`$file_id\`" >> $GITHUB_STEP_SUMMARY - - - name: Publish to Modrinth - if: ${{ inputs.publish_modrinth }} - run: ./gradlew publishModrinth --stacktrace - - - name: Build website update inputs - id: website_inputs - if: ${{ inputs.update_website && inputs.publish_curseforge }} - run: | - MOD_VERSION=$(grep "^mod_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r' | sed 's/-MC.*$//') - MC_VERSION=$(grep "^minecraft_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') - FAPI_VERSION=$(grep "^fabric_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') - JSON_STRING=$(cat << EOF - { - "mod": "mo-glass", - "modloader": "fabric", - "mod_version": "$MOD_VERSION", - "mc_version": "$MC_VERSION", - "fapi_version": "$FAPI_VERSION", - "file_id": "${{ steps.cf_file_id.outputs.file_id }}" - } - EOF - ) - # Convert to single line and escape quotes - echo "json=${JSON_STRING//$'\n'/}" >> "$GITHUB_OUTPUT" - - - name: Trigger website update - id: website_dispatch - if: ${{ inputs.update_website && inputs.publish_curseforge }} - uses: codex-/return-dispatch@v2 - with: - token: ${{ secrets.WIMODS_NET_PUBLISH_TOKEN }} - owner: Wurst-Imperium - repo: wimods.net - ref: master - workflow: add_mod_port.yml - workflow_inputs: ${{ steps.website_inputs.outputs.json }} - - - name: Wait for website update to finish (run ${{ steps.website_dispatch.outputs.run_id }}) - if: ${{ inputs.update_website && inputs.publish_curseforge }} - uses: codex-/await-remote-run@v1 - with: - token: ${{ secrets.WIMODS_NET_PUBLISH_TOKEN }} - owner: Wurst-Imperium - repo: wimods.net - run_id: ${{ steps.website_dispatch.outputs.run_id }} - run_timeout_seconds: 600 # 10 minutes + - name: Echo distinct ID ${{ github.event.inputs.distinct_id }} + run: echo ${{ github.event.inputs.distinct_id }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + # Include all tags in case the new tag already exists. + fetch-tags: true + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + java-version: "21" + distribution: "microsoft" + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Build + run: ./gradlew build --stacktrace --warning-mode=fail + + - name: Create and push tag + run: | + MOD_VERSION=$(grep "^mod_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') + git config --global user.name "Wurst-Bot" + git config --global user.email "contact.wurstimperium@gmail.com" + git tag "v$MOD_VERSION" + git push origin "v$MOD_VERSION" + + - name: Close milestone + if: ${{ inputs.close_milestone }} + run: ./gradlew closeMilestone --stacktrace + + - name: Upload backups + if: ${{ inputs.upload_backups }} + run: ./gradlew uploadBackups --stacktrace + + - name: Publish to GitHub + if: ${{ inputs.publish_github }} + env: + GITHUB_TOKEN: ${{ secrets.MCX_PUBLISH_TOKEN }} + run: ./gradlew github --stacktrace + + - name: Publish to CurseForge + if: ${{ inputs.publish_curseforge }} + run: ./gradlew publishCurseforge --stacktrace + + - name: Get CurseForge file ID + id: cf_file_id + if: ${{ inputs.publish_curseforge }} + run: | + file_id=$(./gradlew getCurseforgeId -x publishCurseforge | grep -o 'CURSEFORGE_FILE_ID=[0-9]*' | grep -o '[0-9]*') + echo "file_id=$file_id" >> "$GITHUB_OUTPUT" + echo "CurseForge file ID: \`$file_id\`" >> $GITHUB_STEP_SUMMARY + + - name: Publish to Modrinth + if: ${{ inputs.publish_modrinth }} + run: ./gradlew publishModrinth --stacktrace + + - name: Build website update inputs + id: website_inputs + if: ${{ inputs.update_website && inputs.publish_curseforge }} + run: | + MOD_VERSION=$(grep "^mod_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r' | sed 's/-MC.*$//') + MC_VERSION=$(grep "^minecraft_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') + FAPI_VERSION=$(grep "^fabric_version=" gradle.properties | cut -d'=' -f2 | tr -d ' \r') + JSON_STRING=$(cat << EOF + { + "mod": "mo-glass", + "modloader": "fabric", + "mod_version": "$MOD_VERSION", + "mc_version": "$MC_VERSION", + "fapi_version": "$FAPI_VERSION", + "file_id": "${{ steps.cf_file_id.outputs.file_id }}" + } + EOF + ) + # Convert to single line and escape quotes + echo "json=${JSON_STRING//$'\n'/}" >> "$GITHUB_OUTPUT" + + - name: Trigger website update + id: website_dispatch + if: ${{ inputs.update_website && inputs.publish_curseforge }} + uses: codex-/return-dispatch@v2 + with: + token: ${{ secrets.WIMODS_NET_PUBLISH_TOKEN }} + owner: Wurst-Imperium + repo: wimods.net + ref: master + workflow: add_mod_port.yml + workflow_inputs: ${{ steps.website_inputs.outputs.json }} + + - name: Wait for website update to finish (run ${{ steps.website_dispatch.outputs.run_id }}) + if: ${{ inputs.update_website && inputs.publish_curseforge }} + uses: codex-/await-remote-run@v1 + with: + token: ${{ secrets.WIMODS_NET_PUBLISH_TOKEN }} + owner: Wurst-Imperium + repo: wimods.net + run_id: ${{ steps.website_dispatch.outputs.run_id }} + run_timeout_seconds: 600 # 10 minutes diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index e7b27b7..a08ccc8 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -5,6 +5,7 @@ on: - cron: "30 1 * * 1-5" permissions: + # Both needed by the stale action. issues: write pull-requests: write @@ -13,30 +14,31 @@ jobs: runs-on: ubuntu-latest steps: - - name: Run stale bot - uses: actions/stale@v9 - with: - stale-issue-message: | - This issue has been open for a while with no recent activity. If this issue is still important to you, please add a comment within the next 7 days to keep it open. Otherwise, the issue will be automatically closed to free up time for other tasks. - - Issues should be closed if: - - They are duplicates of other issues - - There is not enough demand - - They are no longer relevant - - There are not enough details - stale-pr-message: | - This pull request has been open for a while with no recent activity. If you're still working on this or waiting for a review, please add a comment or commit within the next 7 days to keep it open. Otherwise, the pull request will be automatically closed to free up time for other tasks. - - Pull requests should be closed if: - - They have been superseded by another pull request - - They are out of scope or don't align with the project - - They have become obsolete due to other changes - - They have bugs or conflicts that won't be resolved - days-before-stale: 60 - days-before-close: 7 - exempt-issue-labels: "status:never-stale" - exempt-pr-labels: "status:never-stale" - stale-issue-label: "status:stale" - stale-pr-label: "status:stale" - operations-per-run: 30 - enable-statistics: true + - name: Run stale action + uses: actions/stale@v9 + with: + stale-issue-message: | + This issue has been open for a while with no recent activity. If this issue is still important to you, please add a comment within the next 7 days to keep it open. Otherwise, the issue will be automatically closed to free up time for other tasks. + + Issues should be closed if: + - They are duplicates of other issues + - There is not enough demand + - They are no longer relevant + - There are not enough details + stale-pr-message: | + This pull request has been open for a while with no recent activity. If you're still working on this or waiting for a review, please add a comment or commit within the next 7 days to keep it open. Otherwise, the pull request will be automatically closed to free up time for other tasks. + + Pull requests should be closed if: + - They have been superseded by another pull request + - They are out of scope or don't align with the project + - They have become obsolete due to other changes + - They have bugs or conflicts that won't be resolved + days-before-stale: 60 + days-before-close: 7 + exempt-issue-labels: "status:never-stale,status:confirmed" + exempt-pr-labels: "status:never-stale,status:confirmed,status:merged" + exempt-all-milestones: true + stale-issue-label: "status:stale" + stale-pr-label: "status:stale" + operations-per-run: 30 + enable-statistics: true From e9db5da86d634f195c5b0fe1a9ae57ec07b56e36 Mon Sep 17 00:00:00 2001 From: Alexander01998 Date: Sun, 12 Jan 2025 21:52:25 +0100 Subject: [PATCH 3/3] Add check_translations workflow --- .github/workflows/check_translations.yml | 32 ++++++ .gitignore | 3 + build.gradle | 1 + scripts/check_translations.py | 99 +++++++++++++++++++ scripts/util.py | 17 ++++ .../resources/intentionally_untranslated.json | 1 + 6 files changed, 153 insertions(+) create mode 100644 .github/workflows/check_translations.yml create mode 100644 scripts/check_translations.py create mode 100644 scripts/util.py create mode 100644 src/main/resources/intentionally_untranslated.json diff --git a/.github/workflows/check_translations.yml b/.github/workflows/check_translations.yml new file mode 100644 index 0000000..c2b575a --- /dev/null +++ b/.github/workflows/check_translations.yml @@ -0,0 +1,32 @@ +name: Check Translations + +on: + push: + branches-ignore: + - "dependabot/**" + tags-ignore: + - "**" + paths: + - "src/main/resources/assets/mo_glass/lang/**.json" + - "src/main/resources/intentionally_untranslated.json" + pull_request: + paths: + - "src/main/resources/assets/mo_glass/lang/**.json" + - "src/main/resources/intentionally_untranslated.json" + workflow_dispatch: + +jobs: + check_translations: + runs-on: ubuntu-latest + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run check_translations.py + run: python scripts/check_translations.py diff --git a/.gitignore b/.gitignore index d3b435b..066a680 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ run/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +# python +*.pyc + desktop.ini # forge diff --git a/build.gradle b/build.gradle index 8188682..c738777 100644 --- a/build.gradle +++ b/build.gradle @@ -116,6 +116,7 @@ jar { } exclude("/assets/mo_glass/textures/block/*.psd") + exclude("intentionally_untranslated.json") } import com.diffplug.spotless.generic.LicenseHeaderStep diff --git a/scripts/check_translations.py b/scripts/check_translations.py new file mode 100644 index 0000000..9d55a28 --- /dev/null +++ b/scripts/check_translations.py @@ -0,0 +1,99 @@ +import util +from pathlib import Path + +translations_dir = Path("src") / "main" / "resources" / "assets" / "mo_glass" / "lang" + + +def show_translation_stats(en_us: dict, translations: dict): + """Render a table of the current translation progress for each language.""" + util.add_github_summary("| Language | Translated | % |") + util.add_github_summary("| --- | --- | --- |") + util.add_github_summary(f"| en_us | {len(en_us)} | 100.00% |") + for lang, data in translations.items(): + util.add_github_summary(f"| {lang} | {len(data)} | {len(data) / len(en_us) * 100:.2f}% |") + util.add_github_summary("") + + +def check_extra_keys(en_us: dict, translations: dict): + """Check if any translation files contain keys that don't exist in the original.""" + extra_keys_found = False + for lang, data in translations.items(): + extra_keys = set(data.keys()) - set(en_us.keys()) + if extra_keys: + extra_keys_found = True + util.add_github_summary( + f"⚠ {lang}.json contains translations that don't exist in en_us.json ({len(extra_keys)} found):" + ) + for key in extra_keys: + util.add_github_summary(f"- {key}") + if extra_keys_found: + raise Exception("Found extra keys in one or more translation files, see summary") + util.add_github_summary("✅ No extra keys found") + + +def check_untranslated_strings(en_us: dict, translations: dict): + """Check if any translation files contain untranslated strings.""" + untranslated_strings_found = False + intentionally_untranslated = util.read_json_file( + Path("src") / "main" / "resources" / "intentionally_untranslated.json" + ) + + for lang, data in translations.items(): + untranslated_strings = set() + for key, value in data.items(): + if value == en_us[key]: + if lang in intentionally_untranslated and key in intentionally_untranslated[lang]: + continue + untranslated_strings.add(key) + if untranslated_strings: + untranslated_strings_found = True + util.add_github_summary( + f"⚠ {lang}.json contains strings that are identical to en_us.json ({len(untranslated_strings)} found):" + ) + for key in untranslated_strings: + util.add_github_summary(f"- {key}: {en_us[key]}") + util.add_github_summary( + "\nIf this is intentional, add the affected key(s) to intentionally_untranslated.json:" + ) + util.add_github_summary("```json") + util.add_github_summary(f' "{lang}": [') + for key in untranslated_strings: + util.add_github_summary(f' "{key}"') + util.add_github_summary(" ]") + util.add_github_summary("```") + + if untranslated_strings_found: + raise Exception("Found untranslated strings in one or more translation files, see summary") + util.add_github_summary("✅ No accidentally untranslated strings found") + + +def check_order_of_strings(en_us: dict, translations: dict): + """Check if the strings in each translation file are in the same order as in en_us.json.""" + for lang, data in translations.items(): + en_us_keys_present_in_translation = [key for key in en_us.keys() if key in data.keys()] + translation_keys_present_in_en_us = [key for key in data.keys() if key in en_us.keys()] + if en_us_keys_present_in_translation != translation_keys_present_in_en_us: + raise Exception(f"⚠ The order of strings in {lang}.json is different from en_us.json") + util.add_github_summary("✅ The order of strings in each translation file matches en_us.json") + + +def main(): + en_us = util.read_json_file(translations_dir / "en_us.json") + translations = {} + for path in sorted(translations_dir.rglob("*.json"), key=lambda x: x.name): + if path.is_file() and path.name != "en_us.json": + lang = path.name.removesuffix(".json") + data = util.read_json_file(path) + data.pop("language.name", None) + data.pop("language.region", None) + data.pop("language.code", None) + translations[lang] = data + + show_translation_stats(en_us, translations) + check_extra_keys(en_us, translations) + check_untranslated_strings(en_us, translations) + check_order_of_strings(en_us, translations) + + +if __name__ == "__main__": + main() diff --git a/scripts/util.py b/scripts/util.py new file mode 100644 index 0000000..807fce1 --- /dev/null +++ b/scripts/util.py @@ -0,0 +1,17 @@ +import json +import os +from pathlib import Path + + +def read_json_file(path: Path) -> dict: + """Read a JSON data file.""" + return json.loads(path.read_text(encoding="utf-8")) + + +def add_github_summary(summary: str): + """Add a line to the GitHub Actions summary for the current step.""" + if "GITHUB_STEP_SUMMARY" not in os.environ: + print(summary) + return + with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as summary_file: + print(summary, file=summary_file) diff --git a/src/main/resources/intentionally_untranslated.json b/src/main/resources/intentionally_untranslated.json new file mode 100644 index 0000000..69a88e3 --- /dev/null +++ b/src/main/resources/intentionally_untranslated.json @@ -0,0 +1 @@ +{}