diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 5d1cf8a92..18ab88e49 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -58,13 +58,13 @@ jobs: find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \; - name: ๐Ÿ“ค Upload Jar - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: CC-Tweaked path: ./jars - name: ๐Ÿ“ค Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 build-core: strategy: @@ -81,24 +81,28 @@ jobs: runs-on: ${{ matrix.uses }} steps: - - name: Clone repository + - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v4 - - name: Set up Java + - name: ๐Ÿ“ฅ Set up Java uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' - - name: Setup Gradle + - name: ๐Ÿ“ฅ Setup Gradle uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }} - - name: Run tests + - name: โš’๏ธ Build + run: | + ./gradlew --configure-on-demand :core:assemble :web:assemble + + - name: ๐Ÿงช Run tests run: | ./gradlew --configure-on-demand :core:test - - name: Parse test reports + - name: ๐Ÿงช Parse test reports run: python3 ./tools/parse-reports.py if: ${{ failure() }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d2aa3128..5fc93c314 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,9 +29,9 @@ ## Setting up a development environment In order to develop CC: Tweaked, you'll need to download the source code and then run it. - Make sure you've got the following software installed: - - Java Development Kit (JDK) installed. This can be downloaded from [Adoptium]. + - Java Development Kit (JDK). This can be downloaded from [Adoptium]. - [Git](https://git-scm.com/). - - If you want to work on documentation, [NodeJS][node]. + - [NodeJS][node]. - Download CC: Tweaked's source code: ``` diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt index ddbe11dfa..a1da1e862 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt @@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() { @TaskAction fun install() { project.exec { - commandLine("npm", "ci") + commandLine(ProcessHelpers.getExecutable("npm"), "ci") workingDir = projectRoot.get().asFile } } @@ -59,6 +59,6 @@ fun install() { abstract class NpxExecToDir : ExecToDir() { init { dependsOn(NpmInstall.TASK_NAME) - executable = "npx" + executable = ProcessHelpers.getExecutable("npx") } } diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt index 4f3fb2463..c16b55441 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt @@ -9,6 +9,7 @@ import java.io.BufferedReader import java.io.File import java.io.InputStreamReader +import java.nio.charset.StandardCharsets internal object ProcessHelpers { fun startProcess(vararg command: String): Process { @@ -34,7 +35,7 @@ fun captureLines(vararg command: String): List { val process = startProcess(*command) process.outputStream.close() - val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader -> + val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader -> reader.lines().filter { it.isNotEmpty() }.toList() } ProcessGroovyMethods.closeStreams(process) @@ -46,6 +47,28 @@ fun onPath(name: String): Boolean { val path = System.getenv("PATH") ?: return false return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() } } + + /** + * Search for an executable on the `PATH` if required. + * + * [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on + * Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name]. + */ + fun getExecutable(name: String): String { + if (!System.getProperty("os.name").lowercase().contains("windows")) return name + + val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator) + val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator) + + for (pathEntry in path) { + for (ext in pathExt) { + val resolved = File(pathEntry, name + ext) + if (resolved.exists()) return resolved.getAbsolutePath() + } + } + + return name + } } internal fun Process.waitForOrThrow(message: String) { diff --git a/gradle.properties b/gradle.properties index 8363bedf6..52d6d8c3d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MPL-2.0 -org.gradle.jvmargs=-Xmx3G +org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8 org.gradle.parallel=true kotlin.stdlib.default.dependency=false diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua index 43f0e4667..0d6ead5a2 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua @@ -213,16 +213,16 @@ end -- Existing settings will be merged with any pre-existing ones. Conflicting -- entries will be overwritten, but any others will be preserved. -- --- @tparam[opt] string sPath The file to load from, defaulting to `.settings`. +-- @tparam[opt=".settings"] string path The file to load from. -- @treturn boolean Whether settings were successfully read from this -- file. Reasons for failure may include the file not existing or being -- corrupted. -- -- @see settings.save --- @changed 1.87.0 `sPath` is now optional. -function load(sPath) - expect(1, sPath, "string", "nil") - local file = fs.open(sPath or ".settings", "r") +-- @changed 1.87.0 `path` is now optional. +function load(path) + expect(1, path, "string", "nil") + local file = fs.open(path or ".settings", "r") if not file then return false end @@ -255,14 +255,14 @@ end -- This will entirely overwrite the pre-existing file. Settings defined in the -- file, but not currently loaded will be removed. -- --- @tparam[opt] string sPath The path to save settings to, defaulting to `.settings`. +-- @tparam[opt=".settings"] string path The path to save settings to. -- @treturn boolean If the settings were successfully saved. -- -- @see settings.load --- @changed 1.87.0 `sPath` is now optional. -function save(sPath) - expect(1, sPath, "string", "nil") - local file = fs.open(sPath or ".settings", "w") +-- @changed 1.87.0 `path` is now optional. +function save(path) + expect(1, path, "string", "nil") + local file = fs.open(path or ".settings", "w") if not file then return false end diff --git a/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java b/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java index 123e81d7a..23c50616c 100644 --- a/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java +++ b/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java @@ -68,6 +68,10 @@ public void remapClass(String from, String to) { } private @Nullable Path findUnmappedFile(String name) { + // For some odd reason, we try to resolve the generated lambda classes. This includes classes called + // things like lambda, which is an invalid path on Windows. Detect those, and abort early. + if (name.indexOf("<") >= 0) return null; + return classpath.stream().map(x -> x.resolve(name)).filter(Files::exists).findFirst().orElse(null); }