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
196 changes: 196 additions & 0 deletions .github/workflows/mod-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: Mod CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
defaults:
run:
working-directory: mod

steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin all GitHub Actions to commit SHAs.

Using floating tags (@v4) leaves CI vulnerable to upstream action supply-chain drift and violates the repository’s stated policy. Pin each action to an immutable full-length commit SHA.

Suggested hardening diff
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@<full-length-commit-sha>
...
-        uses: actions/setup-java@v4
+        uses: actions/setup-java@<full-length-commit-sha>
...
-        uses: gradle/actions/setup-gradle@v4
+        uses: gradle/actions/setup-gradle@<full-length-commit-sha>
...
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@<full-length-commit-sha>

Also applies to: 23-23, 29-29, 38-38, 78-78, 81-81, 87-87, 103-103

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 20-20: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mod-ci.yml at line 20, The workflow uses floating action
tags like "uses: actions/checkout@v4" (and similar lines at "uses:
actions/setup-node@v4", "uses: actions/cache@v4", etc.) which must be pinned to
immutable full-length commit SHAs; update each "uses: <owner>/<repo>@<tag>"
occurrence referenced in the diff (e.g., actions/checkout@v4,
actions/setup-node@v4, actions/cache@v4 and the other uses lines noted) to the
corresponding full commit SHA for the exact action version, replacing the tag
with the full-length SHA string so every action is referenced by a specific
immutable commit.

Source: Linters/SAST tools


- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: "8.10.2"

- name: Validate matrix and run common tests
run: |
chmod +x ./gradlew
./gradlew validateModMatrix :common:test --stacktrace --info

- name: Upload Gradle reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: mod-validate-gradle-reports
path: |
mod/**/build/reports/**
mod/**/build/test-results/**

build:
runs-on: ubuntu-latest
needs: validate
strategy:
fail-fast: false
matrix:
include:
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
- minecraft-version: "1.21.2"
loader: fabric
- minecraft-version: "1.21.2"
loader: neoforge
- minecraft-version: "1.21.4"
loader: fabric
- minecraft-version: "1.21.4"
loader: forge
- minecraft-version: "1.21.4"
loader: neoforge
- minecraft-version: "1.21.5"
loader: fabric
- minecraft-version: "1.21.5"
loader: forge
- minecraft-version: "1.21.5"
loader: neoforge
- minecraft-version: "1.21.6"
loader: fabric
- minecraft-version: "1.21.6"
loader: forge
- minecraft-version: "1.21.6"
loader: neoforge
- minecraft-version: "1.21.7"
loader: fabric
- minecraft-version: "1.21.7"
loader: forge
- minecraft-version: "1.21.7"
loader: neoforge
- minecraft-version: "1.21.8"
loader: fabric
- minecraft-version: "1.21.8"
loader: forge
- minecraft-version: "1.21.8"
loader: neoforge
- minecraft-version: "1.21.9"
loader: fabric
- minecraft-version: "1.21.9"
loader: forge
- minecraft-version: "1.21.9"
loader: neoforge
- minecraft-version: "1.21.10"
loader: fabric
- minecraft-version: "1.21.10"
loader: forge
- minecraft-version: "1.21.10"
loader: neoforge
- minecraft-version: "1.21.11"
loader: fabric
- minecraft-version: "1.21.11"
loader: forge
- minecraft-version: "1.21.11"
loader: neoforge

defaults:
run:
working-directory: mod

steps:
- uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: "8.10.2"

- name: Build matrix project
id: build
env:
MINECRAFT_VERSION: ${{ matrix.minecraft-version }}
LOADER: ${{ matrix.loader }}
run: |
chmod +x ./gradlew
set -o pipefail
VERSION_KEY="${MINECRAFT_VERSION//./_}"
TASK=":versions:mc_${VERSION_KEY}:${LOADER}:build"
LOG_FILE="build-${MINECRAFT_VERSION}-${LOADER}.log"
echo "Building ${TASK}"
./gradlew "${TASK}" --stacktrace --info 2>&1 | tee "${LOG_FILE}"
echo "log_file=${LOG_FILE}" >> "${GITHUB_OUTPUT}"
Comment on lines +137 to +138

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve log_file output even when Gradle fails.

With pipefail, a failing Gradle command exits before Line 138, so steps.build.outputs.log_file is empty in the failure-summary step and you lose inline log excerpts.

Suggested fix
       - name: Build matrix project
         id: build
@@
         run: |
           chmod +x ./gradlew
           set -o pipefail
           VERSION_KEY="${MINECRAFT_VERSION//./_}"
           TASK=":versions:mc_${VERSION_KEY}:${LOADER}:build"
           LOG_FILE="build-${MINECRAFT_VERSION}-${LOADER}.log"
+          echo "log_file=${LOG_FILE}" >> "${GITHUB_OUTPUT}"
           echo "Building ${TASK}"
-          ./gradlew "${TASK}" --stacktrace --info 2>&1 | tee "${LOG_FILE}"
-          echo "log_file=${LOG_FILE}" >> "${GITHUB_OUTPUT}"
+          set +e
+          ./gradlew "${TASK}" --stacktrace --info 2>&1 | tee "${LOG_FILE}"
+          status=$?
+          set -e
+          exit "${status}"

Also applies to: 146-146

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mod-ci.yml around lines 137 - 138, The pipeline currently
exits on a failing Gradle command before the echo "log_file=${LOG_FILE}" runs;
change the step around the gradlew pipeline to capture the Gradle exit code from
the pipeline (e.g., use bash's PIPESTATUS[0] after ./gradlew ... | tee
"${LOG_FILE}" to get the gradle exit code), always emit echo
"log_file=${LOG_FILE}" to GITHUB_OUTPUT, then exit the step with the captured
code (exit $EXIT_CODE) so steps.build.outputs.log_file is set even when
./gradlew fails; ensure you reference the existing LOG_FILE variable and
preserve the original exit status.


- name: Write failure summary
if: failure()
env:
MINECRAFT_VERSION: ${{ matrix.minecraft-version }}
LOADER: ${{ matrix.loader }}
run: |
LOG_FILE="${{ steps.build.outputs.log_file }}"
VERSION_KEY="${MINECRAFT_VERSION//./_}"
{
echo "### Mod build failed: ${LOADER} / ${MINECRAFT_VERSION}"
echo ""
echo "**Gradle task:** \`:versions:mc_${VERSION_KEY}:${LOADER}:build\`"
echo ""
if [ -f "${LOG_FILE}" ]; then
echo "#### Error highlights"
echo '```text'
grep -E "FAILURE:|What went wrong:|Execution failed for task|error:|Caused by:|> Could not|BUILD FAILED" "${LOG_FILE}" | tail -n 40 || true
echo '```'
echo ""
echo "#### Last 120 log lines"
echo '```text'
tail -n 120 "${LOG_FILE}"
echo '```'
else
echo "No build log was captured."
fi
} >> "${GITHUB_STEP_SUMMARY}"

- name: Upload Gradle reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: mod-${{ matrix.minecraft-version }}-${{ matrix.loader }}-gradle-reports
path: |
mod/**/build/reports/**
mod/**/build/test-results/**
mod/**/build/tmp/**
mod/build-*.log

summary:
runs-on: ubuntu-latest
needs: [validate, build]
if: always()
steps:
- name: Report overall mod CI status
run: |
{
echo "### Mod CI summary"
echo ""
echo "- validate: ${{ needs.validate.result }}"
echo "- build matrix: ${{ needs.build.result }}"
} >> "${GITHUB_STEP_SUMMARY}"

if [ "${{ needs.validate.result }}" != "success" ] || [ "${{ needs.build.result }}" != "success" ]; then
echo "One or more mod CI jobs failed. Open the failed matrix job for the detailed Gradle log summary."
exit 1
fi
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ highlighter/node_modules/
highlighter/dist/
highlighter/*.vsix
npm/node_modules/
npm/*.tgz
npm/*.tgz
mod/.gradle/
mod/build/
mod/**/build/
26 changes: 24 additions & 2 deletions minecraft_script/shell_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .common import COMMON_CONFIG, version
from .config_utils import update_config, reset_config
from .lint import lint_code
from .version_config import breaking_changes_between
from .version_config import breaking_changes_between, load_version_profile
from pathlib import Path


Expand Down Expand Up @@ -80,6 +80,16 @@ def sh_debug(*args) -> None:

def sh_compile(*args) -> None:
# Manage args & parameters:
args = list(args)
minecraft_version = None
if "--minecraft-version" in args:
version_index = args.index("--minecraft-version")
if version_index + 1 >= len(args):
print("No version specified after --minecraft-version.")
exit(1)
minecraft_version = args[version_index + 1]
del args[version_index:version_index + 2]

arg_count = len(args)
if arg_count < 1:
print("No path specified to compile.")
Expand Down Expand Up @@ -114,7 +124,19 @@ def sh_compile(*args) -> None:
with open(source_path, 'rt', encoding='utf-8') as mcs_file:
code = mcs_file.read()

build_datapack(code, datapack_name, str(output_path), verbose, source_path=source_path)
previous_version = COMMON_CONFIG["minecraft_version"]
if minecraft_version is not None:
try:
load_version_profile(minecraft_version)
except FileNotFoundError as error:
print(f"Error: {error}")
exit(1)
COMMON_CONFIG["minecraft_version"] = minecraft_version

try:
build_datapack(code, datapack_name, str(output_path), verbose, source_path=source_path)
finally:
COMMON_CONFIG["minecraft_version"] = previous_version


def sh_config(*args) -> None:
Expand Down
121 changes: 121 additions & 0 deletions mod/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
plugins {
id "java"
id "dev.architectury.loom" version "${architectury_loom_version}" apply false
id "architectury-plugin" version "${architectury_plugin_version}" apply false
}

ext.modSupportedMinecraftVersions = [
"1.21.2",
"1.21.4",
"1.21.5",
"1.21.6",
"1.21.7",
"1.21.8",
"1.21.9",
"1.21.10",
"1.21.11",
]

ext.modLoaders = ["fabric", "forge", "neoforge"]

ext.fabricApiVersions = [
"1.21.2": "0.106.1+1.21.2",
"1.21.4": "0.119.4+1.21.4",
"1.21.5": "0.128.2+1.21.5",
"1.21.6": "0.128.2+1.21.6",
"1.21.7": "0.129.0+1.21.7",
"1.21.8": "0.136.1+1.21.8",
"1.21.9": "0.134.1+1.21.9",
"1.21.10": "0.138.4+1.21.10",
"1.21.11": "0.141.4+1.21.11",
]

ext.forgeVersions = [
"1.21.2": null,
"1.21.4": "1.21.4-54.1.16",
"1.21.5": "1.21.5-55.1.10",
"1.21.6": "1.21.6-56.0.4",
"1.21.7": "1.21.7-57.0.3",
"1.21.8": "1.21.8-58.1.18",
"1.21.9": "1.21.9-59.0.5",
"1.21.10": "1.21.10-60.1.9",
"1.21.11": "1.21.11-61.1.8",
]

ext.neoForgeVersions = [
"1.21.2": "21.2.1-beta",
"1.21.4": "21.4.157",
"1.21.5": "21.5.97",
"1.21.6": "21.6.20-beta",
"1.21.7": "21.7.25-beta",
"1.21.8": "21.8.53",
"1.21.9": "21.9.16-beta",
"1.21.10": "21.10.64",
"1.21.11": "21.11.42",
]

allprojects {
group = mod_group
version = mod_version
}

subprojects { project ->
apply plugin: "java"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.release = 21
}

repositories {
maven { url = "https://maven.fabricmc.net/" }
maven { url = "https://maven.minecraftforge.net/" }
maven { url = "https://maven.neoforged.net/releases/" }
maven { url = "https://maven.architectury.dev/" }
mavenCentral()
}
Comment on lines +76 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove repository declarations; they violate FAIL_ON_PROJECT_REPOS.

The CI pipeline fails because settings.gradle enforces RepositoriesMode.FAIL_ON_PROJECT_REPOS (line 13), but this block redeclares repositories in the project build file. All repositories are already declared in settings.gradle lines 14-20 and are automatically available to all subprojects.

🔧 Proposed fix
     }
-
-    repositories {
-        maven { url = "https://maven.fabricmc.net/" }
-        maven { url = "https://maven.minecraftforge.net/" }
-        maven { url = "https://maven.neoforged.net/releases/" }
-        maven { url = "https://maven.architectury.dev/" }
-        mavenCentral()
-    }
 }
 
 project(":common") {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
repositories {
maven { url = "https://maven.fabricmc.net/" }
maven { url = "https://maven.minecraftforge.net/" }
maven { url = "https://maven.neoforged.net/releases/" }
maven { url = "https://maven.architectury.dev/" }
mavenCentral()
}
🧰 Tools
🪛 GitHub Actions: Mod CI / 1_validate.txt

[error] 77-77: Gradle build failed during project evaluation. Build configured to prefer settings repositories over project repositories, but repository 'maven' was added by build.gradle. (org.gradle.api.InvalidUserCodeException)

🪛 GitHub Actions: Mod CI / validate

[error] 77-77: Gradle configuration failed while evaluating root project 'minecraft-script-mod'. Build was configured to prefer settings repositories over project repositories, but repository 'maven' was added by build file 'build.gradle' (repository mutation disallowed on project).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mod/build.gradle` around lines 76 - 82, Remove the project-level repositories
block that redeclares Maven repos (the repositories { ... } block containing
maven { url = "https://maven.fabricmc.net/" }, maven { url =
"https://maven.minecraftforge.net/" }, maven { url =
"https://maven.neoforged.net/releases/" }, maven { url =
"https://maven.architectury.dev/" }, and mavenCentral()), since settings.gradle
enforces RepositoriesMode.FAIL_ON_PROJECT_REPOS and already declares these
repos; delete that entire repositories block from mod/build.gradle so the
project relies on the repositories provided by settings.gradle.

}

project(":common") {
dependencies {
testImplementation platform("org.junit:junit-bom:5.10.3")
testImplementation "org.junit.jupiter:junit-jupiter"
}

test {
useJUnitPlatform()
}
}

tasks.register("validateModMatrix") {
inputs.file(rootProject.file("../minecraft_script/versions/index.json"))
doLast {
def index = new groovy.json.JsonSlurper().parse(rootProject.file("../minecraft_script/versions/index.json"))
def expectedVersions = index.supported.findAll { it != "26.1" }
if (expectedVersions != modSupportedMinecraftVersions) {
throw new GradleException("modSupportedMinecraftVersions must equal versions/index.json supported minus 26.1. expected=${expectedVersions}, actual=${modSupportedMinecraftVersions}")
}

def missingProjects = []
expectedVersions.each { mcVersion ->
modLoaders.each { loader ->
if (loader == "forge" && forgeVersions[mcVersion] == null) {
return
}
def projectPath = ":versions:mc_${mcVersion.replace(".", "_")}:${loader}"
if (findProject(projectPath) == null) {
missingProjects.add(projectPath)
}
}
}
if (!missingProjects.isEmpty()) {
throw new GradleException("Missing mod matrix projects: ${missingProjects.join(", ")}")
}
}
}
Loading
Loading