Skip to content

Comments

Update to gradle and latest runelite#1630

Merged
chsami merged 15 commits intomainfrom
development
Dec 23, 2025
Merged

Update to gradle and latest runelite#1630
chsami merged 15 commits intomainfrom
development

Conversation

@chsami
Copy link
Owner

@chsami chsami commented Dec 23, 2025

update to gradle and latest runelite

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Walkthrough

This pull request performs a comprehensive migration of the build system from Maven to Gradle. It introduces new Gradle Kotlin DSL build configurations across multiple modules (cache, runelite-api, runelite-client, runelite-jshell), develops a custom runelite-gradle-plugin with specialized tasks for assembly, component generation, indexing, and JAR signing. All GitHub Actions workflows are updated to invoke Gradle builds instead of Maven commands. Maven-specific configuration files are removed, and dependency and version management are migrated from pom.xml to gradle.properties and libs.versions.toml. The project structure is reorganized using Gradle composite builds, and dependency verification metadata is introduced.

Possibly related PRs

Based on the code-level connections available, no PRs with strong technical relevance to this build system migration were identified. The retrieved candidates primarily involve minor version property adjustments or isolated example script modifications that do not represent substantial code-level coupling with the Gradle migration infrastructure.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main objective of the changeset: migrating the build system from Maven to Gradle and updating to the latest RuneLite version.
Description check ✅ Passed The description is concise and directly related to the changeset, accurately reflecting the primary changes to Gradle and RuneLite updates.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java (1)

22-60: Documentation is inconsistent with implementation.

The Javadoc describes a "Performance test script for measuring GameObject composition retrieval speed" that "runs every 5 seconds," but the actual implementation:

  • Runs every 1 second (1000ms), not 5 seconds
  • Opens a bank and withdraws coins instead of testing GameObject performance
  • Contains no timing measurements or GameObject retrieval logic
runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java (1)

61-66: listFiles() can return null, causing NPE in for-each.

File.listFiles() returns null if the path isn't a directory or an I/O error occurs. The for-each on lines 61 and 66 would throw NullPointerException in that case, which is inconsistent with the declared IOException handling.

🔎 Suggested null guard
-		for (File indexFolder : archiveOverlayDirectory.listFiles())
+		File[] indexFolders = archiveOverlayDirectory.listFiles();
+		if (indexFolders == null)
+		{
+			throw new IOException("Cannot list directory: " + archiveOverlayDirectory);
+		}
+		for (File indexFolder : indexFolders)
 		{
 			if (indexFolder.isDirectory())
 			{
-				for (File archiveFile : indexFolder.listFiles())
+				File[] archiveFiles = indexFolder.listFiles();
+				if (archiveFiles == null)
+				{
+					throw new IOException("Cannot list directory: " + indexFolder);
+				}
+				for (File archiveFile : archiveFiles)
 				{
🧹 Nitpick comments (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java (1)

46-47: Consider using the logger instead of System.out.println.

The script already uses @Slf4j (line 3) and logs errors via log.error (line 58). For consistency, consider using log.info("hello world") instead of System.out.println.

runelite-client/build.gradle.kts (1)

137-139: Inconsistent classifier naming between task property and filename.

archiveClassifier is set to "shadow" but archiveFileName hardcodes "-shaded.jar", and the publishing artifact (line 147) also uses "shaded". Either align everything to "shadow" or "shaded".

Proposed fix
-    archiveClassifier = "shadow"
-    archiveFileName = project.name + "-" + project.version + "-shaded.jar"
+    archiveClassifier = "shaded"
+    archiveFileName = project.name + "-" + project.version + "-shaded.jar"
common.settings.gradle.kts (1)

51-51: checkstyleDir is declared but never used.

Line 51 defines checkstyleDir pointing to "./config/checkstyle/", but line 80 hardcodes file("../config/checkstyle") instead of referencing it. Either use the variable or remove the declaration.

Proposed fix
 // shared checkstyle config
 extensions.findByType<CheckstyleExtension>()?.run {
     toolVersion = "8.3"
-    configDirectory = file("../config/checkstyle")
+    configDirectory = checkstyleDir
 }

Also applies to: 78-80

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 972db0e and d709788.

⛔ Files ignored due to path filters (1)
  • gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (55)
  • .github/workflows/CI.yml
  • .github/workflows/manual_nightly.yml
  • .github/workflows/nightly.yml
  • .github/workflows/release.yml
  • .gitignore
  • .mvn/jvm.config
  • build.gradle.kts
  • cache/build.gradle.kts
  • cache/settings.gradle.kts
  • ci/build.sh
  • ci/settings.xml
  • common.settings.gradle.kts
  • config/checkstyle/checkstyle.xml
  • config/checkstyle/suppressions.xml
  • gradle.properties
  • gradle/verification-metadata.xml
  • gradle/wrapper/gradle-wrapper.properties
  • gradlew
  • gradlew.bat
  • libs.versions.toml
  • runelite-api/build.gradle.kts
  • runelite-api/settings.gradle.kts
  • runelite-client/build.gradle.kts
  • runelite-client/pom.xml
  • runelite-client/settings.gradle.kts
  • runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java
  • runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/LocationStartNotificationOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/README.md
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleConfig.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleRequirements.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleTasks.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/SpellbookOption.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/UnifiedLocation.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-gradle-plugin/pmd-ruleset.xml
  • runelite-gradle-plugin/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignExtension.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java
  • runelite-jshell/build.gradle.kts
  • runelite-jshell/settings.gradle.kts
  • settings.gradle.kts
💤 Files with no reviewable changes (13)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleScript.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleRequirements.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleTasks.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/README.md
  • ci/settings.xml
  • .mvn/jvm.config
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/LocationStartNotificationOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleConfig.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/UnifiedLocation.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleOverlay.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/SpellbookOption.java
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

**/*.java: Keep indentation with tabs, follow the brace placement already in MicrobotPlugin.java, and prefer lines under 120 characters
Use UpperCamelCase for types, lowerCamelCase for members, and prefix configuration interfaces with the plugin name (e.g., ExampleConfig)
Rely on Lombok for boilerplate where already adopted

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java
  • runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignExtension.java
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java
ci/build.sh

📄 CodeRabbit inference engine (AGENTS.md)

./ci/build.sh recreates the CI pipeline, fetching glslangValidator and running mvn verify --settings ci/settings.xml

Files:

  • ci/build.sh
runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java: Gameplay automation lives in runelite-client/src/main/java/net/runelite/client/plugins/microbot; keep new scripts and utilities inside this plugin
Register new automation under net.runelite.client.plugins.microbot and reuse the scheduler pattern shown in ExampleScript

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

Runnable examples live in .../microbot/example

Files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/**/*.java : Shared helpers sit under `.../microbot/util`
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java : Gameplay automation lives in `runelite-client/src/main/java/net/runelite/client/plugins/microbot`; keep new scripts and utilities inside this plugin
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to pom.xml : The root `pom.xml` controls the multi-module Maven build for `cache`, `runelite-api`, `runelite-client`, `runelite-jshell`, and `runelite-maven-plugin`
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to pom.xml : The root `pom.xml` controls the multi-module Maven build for `cache`, `runelite-api`, `runelite-client`, `runelite-jshell`, and `runelite-maven-plugin`

Applied to files:

  • cache/settings.gradle.kts
  • runelite-client/pom.xml
  • .github/workflows/nightly.yml
  • libs.versions.toml
  • runelite-api/settings.gradle.kts
  • runelite-client/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • gradle.properties
  • runelite-gradle-plugin/settings.gradle.kts
  • settings.gradle.kts
  • cache/build.gradle.kts
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-jshell/build.gradle.kts
  • common.settings.gradle.kts
  • runelite-client/build.gradle.kts
  • .gitignore
  • runelite-gradle-plugin/pmd-ruleset.xml
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/**/*.java : Shared helpers sit under `.../microbot/util`

Applied to files:

  • runelite-client/pom.xml
  • libs.versions.toml
  • runelite-api/settings.gradle.kts
  • runelite-client/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • gradle.properties
  • runelite-gradle-plugin/settings.gradle.kts
  • settings.gradle.kts
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • cache/build.gradle.kts
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-client/build.gradle.kts
  • .gitignore
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: `mvn -pl runelite-client -am package` builds the client and produces `target/microbot-<version>.jar`

Applied to files:

  • runelite-client/pom.xml
  • .github/workflows/nightly.yml
  • .github/workflows/manual_nightly.yml
  • runelite-client/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • gradle.properties
  • settings.gradle.kts
  • .github/workflows/release.yml
  • runelite-api/build.gradle.kts
  • runelite-client/build.gradle.kts
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: `java -jar runelite-client/target/microbot-<version>.jar` launches a locally built client for manual validation

Applied to files:

  • runelite-client/pom.xml
  • .github/workflows/nightly.yml
  • .github/workflows/manual_nightly.yml
  • runelite-client/settings.gradle.kts
  • gradle.properties
  • .github/workflows/release.yml
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java : Gameplay automation lives in `runelite-client/src/main/java/net/runelite/client/plugins/microbot`; keep new scripts and utilities inside this plugin

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/settings.gradle.kts
  • gradle.properties
  • settings.gradle.kts
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-client/build.gradle.kts
  • .gitignore
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/**/*.java : Runnable examples live in `.../microbot/example`

Applied to files:

  • runelite-client/pom.xml
  • runelite-client/settings.gradle.kts
  • gradle.properties
  • settings.gradle.kts
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
  • cache/build.gradle.kts
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-client/build.gradle.kts
📚 Learning: 2025-08-31T19:19:07.691Z
Learnt from: chsami
Repo: chsami/Microbot PR: 1455
File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotVersionChecker.java:112-132
Timestamp: 2025-08-31T19:19:07.691Z
Learning: The Microbot project uses only numeric versioning (e.g., "1.2.3") without any alphanumeric suffixes like "-beta" or "-alpha", so simple integer parsing of version segments is safe.

Applied to files:

  • runelite-client/pom.xml
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Centralize shared logic in util classes rather than duplicating inside scripts; inject dependencies through RuneLite's DI when needed

Applied to files:

  • libs.versions.toml
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/test/java/**/*.java : Tests mirror sources in `runelite-client/src/test/java`

Applied to files:

  • runelite-client/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • gradle.properties
  • settings.gradle.kts
  • cache/build.gradle.kts
  • runelite-api/build.gradle.kts
  • config/checkstyle/suppressions.xml
  • runelite-client/build.gradle.kts
  • .gitignore
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/test/java/**/*.java : Write JUnit 4 tests (`junit:4.12`) under matching package paths in `runelite-client/src/test/java`

Applied to files:

  • runelite-client/settings.gradle.kts
  • gradle.properties
  • settings.gradle.kts
  • cache/build.gradle.kts
  • runelite-gradle-plugin/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-client/build.gradle.kts
  • .gitignore
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: `mvn -pl runelite-client test` runs the unit suite; add `-DskipTests` only when packaging binaries for distribution

Applied to files:

  • runelite-client/settings.gradle.kts
  • runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java
  • settings.gradle.kts
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to ci/build.sh : `./ci/build.sh` recreates the CI pipeline, fetching `glslangValidator` and running `mvn verify --settings ci/settings.xml`

Applied to files:

  • ci/build.sh
  • .github/workflows/CI.yml
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/**/*.java : Register new automation under `net.runelite.client.plugins.microbot` and reuse the scheduler pattern shown in `ExampleScript`

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to runelite-client/src/main/java/net/runelite/client/plugins/microbot/*Plugin.java : When adding panel controls or overlays, update the Microbot navigation panel setup in `MicrobotPlugin` and provide default config values

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
📚 Learning: 2025-08-23T16:17:37.615Z
Learnt from: g-mason0
Repo: chsami/Microbot PR: 1423
File: runelite-client/src/main/java/net/runelite/client/plugins/microbot/externalplugins/MicrobotPluginManager.java:366-389
Timestamp: 2025-08-23T16:17:37.615Z
Learning: The ScheduledExecutorService injected into MicrobotPluginManager (and other RuneLite classes) is configured as a single-threaded executor via Executors.newSingleThreadScheduledExecutor() at RuneLiteModule.java line 130, wrapped in ExecutorServiceExceptionLogger. This ensures all async tasks execute sequentially, preventing race conditions.

Applied to files:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to **/*.java : Rely on Lombok for boilerplate where already adopted

Applied to files:

  • cache/build.gradle.kts
  • runelite-api/build.gradle.kts
  • runelite-jshell/build.gradle.kts
📚 Learning: 2025-11-24T19:05:32.967Z
Learnt from: CR
Repo: chsami/Microbot PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:05:32.967Z
Learning: Applies to docs/**/* : Project documentation and walkthroughs are kept in `docs/`

Applied to files:

  • .gitignore
🧬 Code graph analysis (5)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java (1)
  • Plugin (33-80)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java (3)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java (1)
  • CacheableTask (51-172)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java (1)
  • CacheableTask (42-87)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java (1)
  • CacheableTask (38-94)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java (1)
  • Plugin (33-80)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java (1)
  • Plugin (33-80)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java (3)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java (1)
  • CacheableTask (57-182)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java (1)
  • CacheableTask (51-172)
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java (1)
  • CacheableTask (38-94)
🪛 actionlint (1.7.9)
.github/workflows/nightly.yml

37-37: shellcheck reported issue in this script: SC2086:info:2:60: Double quote to prevent globbing and word splitting

(shellcheck)

.github/workflows/manual_nightly.yml

40-40: shellcheck reported issue in this script: SC2086:info:2:60: Double quote to prevent globbing and word splitting

(shellcheck)

.github/workflows/release.yml

29-29: shellcheck reported issue in this script: SC2086:info:2:60: Double quote to prevent globbing and word splitting

(shellcheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (18)
runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java (1)

45-45: LGTM!

The metadata field addition is clean and correctly positioned. With @AllArgsConstructor, the constructor signature properly includes this new parameter in the expected order.

runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java (1)

697-697: LGTM!

The parameter mapping between the method signature and constructor call is correct. The metadata parameter is properly passed through to the LootReceived event despite the different ordering in the method and constructor signatures.

runelite-gradle-plugin/pmd-ruleset.xml (1)

31-33: No inconsistent logic detected.

Text update aligns with the Maven to Gradle migration.

runelite-client/pom.xml (1)

44-44: Version bump looks good.

runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java (1)

35-82: LGTM!

Plugin wiring is logically consistent: extension conventions flow correctly to per-jar sign tasks, and task dependencies are properly established.

.gitignore (1)

1-40: LGTM!

Gitignore structure is logically consistent with the Maven→Gradle transition, appropriately handling both build systems.

gradle/verification-metadata.xml (1)

1-14: LGTM!

Dependency verification metadata is well-structured. Having hashes populated while verification is disabled allows for staged rollout.

cache/build.gradle.kts (1)

26-117: LGTM!

Build configuration is logically sound. ANTLR source generation, Lombok configuration, and task dependencies are wired consistently.

runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java (1)

51-172: LGTM!

Task logic is internally consistent: TOML parsing, ID validation, duplicate detection, and code generation flow logically.

runelite-jshell/settings.gradle.kts (1)

26-27: LGTM - Settings file correctly configured.

The module settings properly reference the centralized configuration and set an appropriate project name.

settings.gradle.kts (1)

26-31: Root composite build structure looks good.

The multi-module setup correctly includes all modules that were previously managed by the root Maven POM, with the gradle plugin properly replacing the maven plugin.

ci/build.sh (1)

18-19: CI build script successfully migrated to Gradle.

The environment variable correctly passes the GLSLang validator path to Gradle, and the buildAll task replaces the Maven verify goal appropriately.

gradle/wrapper/gradle-wrapper.properties (1)

1-7: Standard Gradle wrapper configuration.

The wrapper properties correctly specify Gradle 8.8 with SHA256 verification enabled, following Gradle best practices.

runelite-client/settings.gradle.kts (1)

26-30: Client module settings correctly configured.

The composite build includes are appropriate for standalone module builds while the common settings ensure consistency across the project.

cache/settings.gradle.kts (1)

26-27: Cache module settings are appropriately minimal.

The configuration correctly applies common settings without unnecessary composite build dependencies.

gradle.properties (1)

26-38: Gradle properties appropriately configured.

The build configuration, project coordinates, and placeholder values for credentials are consistent with standard Gradle practices where sensitive values are injected via environment variables or CI secrets.

runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java (1)

32-44: Plugin implementation follows Gradle best practices.

The task registration uses the TaskProvider API correctly, and the dependency wiring ensures the assembly task runs before resource processing, which is the correct build order.

runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java (1)

57-181: LGTM!

The task implementation follows consistent patterns with sibling tasks (IndexTask, ComponentTask) and properly uses Gradle's @CacheableTask, abstract property accessors, and @TaskAction annotation.

Comment on lines +52 to +56
- name: Prepare artifact name
run: |
SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent glob handling for artifact preparation.

The SRC variable uses glob expansion via echo, but the subsequent cp command expects exactly one file. If the glob matches zero or multiple files, the copy will fail silently or incorrectly.

🔎 Proposed fix
      - name: Prepare artifact name
        run: |
-          SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
+          SRC=$(ls runelite-client/build/libs/client-*-shaded.jar 2>/dev/null | head -n1)
+          if [ -z "$SRC" ]; then echo "No shaded JAR found"; exit 1; fi
          DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
          cp "$SRC" "$DEST"
📝 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
- name: Prepare artifact name
run: |
SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
- name: Prepare artifact name
run: |
SRC=$(ls runelite-client/build/libs/client-*-shaded.jar 2>/dev/null | head -n1)
if [ -z "$SRC" ]; then echo "No shaded JAR found"; exit 1; fi
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
🤖 Prompt for AI Agents
.github/workflows/manual_nightly.yml around lines 52 to 56: the current script
uses `SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)` which relies
on `echo` and can yield zero or multiple matches while `cp` expects a single
file; replace this with proper glob handling that verifies exactly one file
before copying — expand the glob in the shell (e.g., use an array or `set --
runelite-client/build/libs/client-*-shaded.jar`), check the match count (fail
the job if zero or >1 with a clear error), then set DEST and run `cp` using the
verified single path.

Comment on lines +49 to +53
- name: Prepare artifact name
run: |
SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Glob may match multiple files or none, causing silent misbehavior.

The echo client-*-shaded.jar pattern assumes exactly one match. If multiple shaded JARs exist (or none), SRC will contain a space-separated list (or the literal glob), causing cp to fail or copy the wrong file.

🔎 Suggested hardening
      - name: Prepare artifact name
        run: |
-          SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
+          shopt -s nullglob
+          FILES=(runelite-client/build/libs/client-*-shaded.jar)
+          if [ "${#FILES[@]}" -ne 1 ]; then
+            echo "Expected exactly 1 shaded jar, found ${#FILES[@]}" >&2
+            exit 1
+          fi
+          SRC="${FILES[0]}"
           DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
           cp "$SRC" "$DEST"
📝 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
- name: Prepare artifact name
run: |
SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
- name: Prepare artifact name
run: |
shopt -s nullglob
FILES=(runelite-client/build/libs/client-*-shaded.jar)
if [ "${#FILES[@]}" -ne 1 ]; then
echo "Expected exactly 1 shaded jar, found ${#FILES[@]}" >&2
exit 1
fi
SRC="${FILES[0]}"
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
🤖 Prompt for AI Agents
.github/workflows/nightly.yml around lines 49-53: the glob assignment to SRC can
expand to zero or multiple matches causing cp to misbehave; change the script to
enable safe globbing, capture matches into an array, verify the array length is
exactly 1 (exit with an error message if 0 or >1), then set SRC to the single
matched path and proceed to cp DEST — this ensures you fail fast on no/multiple
matches instead of silently copying the wrong file.

Comment on lines +42 to +46
- name: Prepare artifact name
run: |
SRC=$(echo runelite-client/build/libs/client-*-shaded.jar)
DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar
cp "$SRC" "$DEST"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same glob assumption issue as in nightly.yml.

The echo client-*-shaded.jar pattern assumes exactly one match exists. Consider adding validation to fail fast if zero or multiple files match the pattern.

🤖 Prompt for AI Agents
In .github/workflows/release.yml around lines 42 to 46, the script assumes the
glob runelite-client/build/libs/client-*-shaded.jar matches exactly one file;
modify the step to expand the glob into an array, check the number of matches,
and fail fast if the count is zero or greater than one (printing the count and
the matching filenames), otherwise proceed to set DEST and copy the single
matched file; ensure the step returns a non‑zero exit code on failure so the
workflow stops.

Comment on lines +31 to +34
<suppress files="[^/\\]+\.java" checks=".*"/>
<suppress files="net[/\\]runelite[/\\]client[/\\]party[/\\]Party\.java" checks=".*"/>
<suppress files="net[/\\]runelite[/\\]api[/\\]widgets[/\\]ComponentID\.java" checks=".*"/>
<suppress files="net[/\\]runelite[/\\]api[/\\]widgets[/\\]InterfaceID\.java" checks=".*"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent suppression rules: broad pattern makes specific rules redundant.

The pattern [^/\\]+\.java on line 31 matches any Java filename (the portion without path separators), effectively suppressing checkstyle for all .java files. The specific suppressions on lines 33-34 for ComponentID.java and InterfaceID.java are therefore redundant.

If the intent is to suppress all Java files, remove lines 33-34. If only specific files should be suppressed, remove or narrow line 31.

🤖 Prompt for AI Agents
In config/checkstyle/suppressions.xml around lines 31 to 34 the rule `<suppress
files="[^/\\]+\.java" checks=".*"/>` is overly broad and already matches any
Java filename, making the later specific suppressions for
net/runelite/.../ComponentID.java and InterfaceID.java redundant; either remove
lines 33-34 (the two specific suppress entries) if the intent is to suppress all
top-level Java filenames, or narrow line 31 to a more specific path pattern that
does not match those files (e.g., include path separators or restrict to a
particular directory) so the specific suppressions remain meaningful.

Comment on lines +38 to +92
@CacheableTask
public abstract class JarsignTask extends DefaultTask
{

public JarsignTask()
{
dependsOn(getBuildTask());
onlyIf(
"target archive must be specified",
_t -> getArchive().getAsFile().get().exists()
);

onlyIf(
"keystore properties are set",
_t ->
getKeystore().isPresent() &&
getStorePass().isPresent() &&
getKeyPass().isPresent() &&
getAlias().isPresent()
);
}

@Input
public abstract Property<Jar> getBuildTask();

@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getArchive();

@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getKeystore();

@Input
public abstract Property<String> getStorePass();

@Input
public abstract Property<String> getKeyPass();

@Input
public abstract Property<String> getAlias();

@TaskAction
public void signArtifact()
{
getProject().exec(exec ->
exec.commandLine(
"jarsigner",
"-keystore", getKeystore().getAsFile().get().getAbsolutePath(),
"-storepass", getStorePass().get(),
"-keypass", getKeyPass().get(),
getArchive().getAsFile().get().getAbsolutePath(),
getAlias().get()
));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing output declaration for cacheable task.

@CacheableTask requires outputs to be declared so Gradle can determine if the task is up-to-date and cache its results. Since signArtifact() modifies the archive in place, the archive should be declared as an output (e.g., @OutputFile). Without this, caching and incremental builds won't work correctly.

🔎 Proposed fix

Change the @InputFile on the archive to @OutputFile (since the file is modified in place):

-	@InputFile
-	@PathSensitive(PathSensitivity.RELATIVE)
+	@OutputFile
 	public abstract RegularFileProperty getArchive();
📝 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
@CacheableTask
public abstract class JarsignTask extends DefaultTask
{
public JarsignTask()
{
dependsOn(getBuildTask());
onlyIf(
"target archive must be specified",
_t -> getArchive().getAsFile().get().exists()
);
onlyIf(
"keystore properties are set",
_t ->
getKeystore().isPresent() &&
getStorePass().isPresent() &&
getKeyPass().isPresent() &&
getAlias().isPresent()
);
}
@Input
public abstract Property<Jar> getBuildTask();
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getArchive();
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getKeystore();
@Input
public abstract Property<String> getStorePass();
@Input
public abstract Property<String> getKeyPass();
@Input
public abstract Property<String> getAlias();
@TaskAction
public void signArtifact()
{
getProject().exec(exec ->
exec.commandLine(
"jarsigner",
"-keystore", getKeystore().getAsFile().get().getAbsolutePath(),
"-storepass", getStorePass().get(),
"-keypass", getKeyPass().get(),
getArchive().getAsFile().get().getAbsolutePath(),
getAlias().get()
));
}
@CacheableTask
public abstract class JarsignTask extends DefaultTask
{
public JarsignTask()
{
dependsOn(getBuildTask());
onlyIf(
"target archive must be specified",
_t -> getArchive().getAsFile().get().exists()
);
onlyIf(
"keystore properties are set",
_t ->
getKeystore().isPresent() &&
getStorePass().isPresent() &&
getKeyPass().isPresent() &&
getAlias().isPresent()
);
}
@Input
public abstract Property<Jar> getBuildTask();
@OutputFile
public abstract RegularFileProperty getArchive();
@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
public abstract RegularFileProperty getKeystore();
@Input
public abstract Property<String> getStorePass();
@Input
public abstract Property<String> getKeyPass();
@Input
public abstract Property<String> getAlias();
@TaskAction
public void signArtifact()
{
getProject().exec(exec ->
exec.commandLine(
"jarsigner",
"-keystore", getKeystore().getAsFile().get().getAbsolutePath(),
"-storepass", getStorePass().get(),
"-keypass", getKeyPass().get(),
getArchive().getAsFile().get().getAbsolutePath(),
getAlias().get()
));
}
🤖 Prompt for AI Agents
In
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java
around lines 38 to 92, the task is annotated with @CacheableTask but the archive
property is declared as @InputFile even though signArtifact() mutates the
archive in place; change the archive declaration to @OutputFile (keep
PathSensitive if desired) so Gradle knows the task produces/updates the archive,
remove the @InputFile annotation from getArchive(), and ensure any logic that
checks archive existence still works with the RegularFileProperty typed as an
output.


public JarsignTask()
{
dependsOn(getBuildTask());
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix: dependsOn cannot accept a Property<Jar>.

The dependsOn() method expects a Task or TaskProvider, but getBuildTask() returns a Property<Jar>. This will fail at configuration time.

🔎 Proposed fix
-		dependsOn(getBuildTask());
+		dependsOn(getBuildTask().get());
📝 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
dependsOn(getBuildTask());
dependsOn(getBuildTask().get());
🤖 Prompt for AI Agents
In
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java
around line 44, the call dependsOn(getBuildTask()) passes a Property<Jar> which
dependsOn does not accept; replace the build-task property with a
TaskProvider<Jar> (or expose the TaskProvider) and wire it so dependsOn receives
that TaskProvider (or alternatively call dependsOn(getBuildTask().get()) if you
accept the eager get()); update the property type and usages accordingly so
dependsOn receives a Task/TaskProvider rather than a Property<Jar>.

Comment on lines +45 to +48
onlyIf(
"target archive must be specified",
_t -> getArchive().getAsFile().get().exists()
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Inconsistent logic: archive existence check is backward.

The onlyIf condition checks whether the archive already exists, but the archive is supposed to be created by the buildTask dependency. If the archive doesn't exist when this task is evaluated, the task won't run—even though signing is precisely what should happen after the build task produces the archive.

🔎 Consider removing this condition or inverting the logic

If you want to skip signing when the jar task is up-to-date and didn't produce output, rely on Gradle's built-in task dependency and incremental build logic instead:

-		onlyIf(
-			"target archive must be specified",
-			_t -> getArchive().getAsFile().get().exists()
-		);

Alternatively, if this check is to validate configuration, move it to a @TaskAction precondition or validation method.

📝 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
onlyIf(
"target archive must be specified",
_t -> getArchive().getAsFile().get().exists()
);
🤖 Prompt for AI Agents
In
runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java
around lines 45-48 the onlyIf check is inverted and prevents the signing task
from running when the archive does not yet exist; remove this onlyIf condition
entirely so the task will always be considered (let Gradle handle
up-to-date/incremental logic via the buildTask dependency), or if you intended
to validate configuration instead, move a presence check into a @TaskAction
precondition/validation method that throws a clear exception rather than using
onlyIf.

@chsami chsami merged commit fa4f1c6 into main Dec 23, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants