diff --git a/.github/buildcheck/tests/test_versions.py b/.github/buildcheck/tests/test_versions.py index b2f03ca0..3504e253 100644 --- a/.github/buildcheck/tests/test_versions.py +++ b/.github/buildcheck/tests/test_versions.py @@ -4,6 +4,7 @@ import io import os +import tempfile import unittest from unittest import mock @@ -62,6 +63,33 @@ def test_only_coordinates_both_use_and_only_differences(self): ["s:p is 2 here and 9 in r1: bump it in every repository"]) +class GradleKotlinTest(unittest.TestCase): + + def test_plugins_are_read_from_every_kts_file_outside_build_output(self): + with tempfile.TemporaryDirectory() as root: + os.makedirs(os.path.join(root, "app", "build")) + with open(os.path.join(root, "app", "build.gradle.kts"), "w", encoding="utf-8") as f: + f.write('plugins {\n id("com.android.application") version "9.4.0"\n' + ' id("org.jetbrains.kotlin.plugin.compose") version "2.4.20"\n' + ' kotlin("jvm") version "2.4.21"\n}\n') + with open(os.path.join(root, "app", "build", "stale.gradle.kts"), "w", encoding="utf-8") as f: + f.write('kotlin("jvm") version "1.0.0"\n') + self.assertEqual(versions.gradle_kotlin_plugins(root), [ + ("app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20"), + ("app/build.gradle.kts", "org.jetbrains.kotlin.jvm", "2.4.21"), + ]) + + def test_a_plugin_behind_the_poms_kotlin_is_a_warning_and_nothing_else_is(self): + own = {"org.jetbrains.kotlin:kotlin-stdlib": {"2.4.21"}, "org.jetbrains.kotlinx:kotlinx-coroutines-core": {"1.11.0"}} + plugins = [("app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20"), + ("app/build.gradle.kts", "org.jetbrains.kotlin.jvm", "2.4.21")] + self.assertEqual(versions.compare_gradle_kotlin(own, plugins), [ + "org.jetbrains.kotlin.plugin.compose is 2.4.20 in app/build.gradle.kts and the POMs use Kotlin 2.4.21: " + "Dependabot bumps only the POMs, bump the Gradle plugin with them"]) + self.assertEqual(versions.compare_gradle_kotlin({"a:b": {"1"}}, plugins), []) + self.assertEqual(versions.compare_gradle_kotlin(own, []), []) + + class MainTest(unittest.TestCase): def run_main(self, siblings): @@ -87,6 +115,19 @@ def test_differences_are_warnings_and_never_fail(self): self.assertNotIn("BitcoinAddressFinder:", err) self.assertIn("1 version differences", out) + def test_a_gradle_kotlin_plugin_behind_the_poms_is_reported_with_the_other_warnings(self): + kotlin = pom('org.jetbrains.kotlin' + 'kotlin-stdlib2.4.21') + out, err = io.StringIO(), io.StringIO() + with mock.patch.object(versions, "current_repo", return_value="java-llama.cpp"), \ + mock.patch.object(versions, "read_local", return_value=[kotlin]), \ + mock.patch.object(versions, "gradle_kotlin_plugins", return_value=[ + ("android-llmservice/app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20")]): + self.assertEqual(versions.main(["x"], "/nowhere", lambda repo, path: kotlin, out, err), 0) + self.assertIn("::warning::org.jetbrains.kotlin.plugin.compose is 2.4.20 in android-llmservice/app/build.gradle.kts " + "and the POMs use Kotlin 2.4.21", err.getvalue()) + self.assertIn("1 version differences", out.getvalue()) + def test_a_missing_pom_skips_that_file_not_the_repository(self): out, err = io.StringIO(), io.StringIO() bumped = pom('org.junit.jupiter' diff --git a/.github/buildcheck/versions.py b/.github/buildcheck/versions.py index 2935a761..7b1270d8 100644 --- a/.github/buildcheck/versions.py +++ b/.github/buildcheck/versions.py @@ -12,12 +12,17 @@ `${property}` references resolved -- not property names, which differ between repositories and include values that must differ (the Java release). +It also compares the Kotlin the POMs use with every Kotlin Gradle plugin a `*.gradle.kts` of the +repository pins: Dependabot's maven ecosystem bumps `kotlin.version` and never sees the Gradle files, +which is how a Compose compiler plugin stayed one patch release behind the POMs. + Warnings, never failures: a bump lands in four pull requests, and the first one merged would otherwise turn the other three repositories red. The repository's own artifacts (`net.ladenthin:*`) are left out: a sibling depends on the last RELEASE of java-llama.cpp while java-llama.cpp itself is at the next snapshot. """ +import os import re import sys import urllib.request @@ -28,7 +33,7 @@ NS = "{http://maven.apache.org/POM/4.0.0}" POMS = { "java-llama.cpp": ("pom.xml", "llama/pom.xml", "llama-langchain4j/pom.xml", "llama-kotlin/pom.xml", - "llama-platform/pom.xml", "llama-atmosphere-agent/pom.xml"), + "llama-platform/pom.xml", "llama-atmosphere-agent/pom.xml", "examples/server/pom.xml"), "BitcoinAddressFinder": ("pom.xml",), "srcmorph": ("pom.xml", "srcmorph/pom.xml", "srcmorph-cli/pom.xml", "srcmorph-maven-plugin/pom.xml"), "streambuffer": ("pom.xml",), @@ -42,6 +47,11 @@ CONFIGURED = {"palantirJavaFormat": "com.palantir.javaformat:palantir-java-format"} RAW_URL = "https://raw.githubusercontent.com/{owner}/{repo}/HEAD/{path}" PROPERTY = re.compile(r"\$\{([^}]+)\}") +KOTLIN_GROUP = "org.jetbrains.kotlin" +# `id("org.jetbrains.kotlin.plugin.compose") version "2.4.21"` and `kotlin("jvm") version "2.4.21"` +GRADLE_KOTLIN = re.compile( + r'(?:id\("(org\.jetbrains\.kotlin(?:\.[A-Za-z.]+)?)"\)|kotlin\("([A-Za-z.]+)"\))\s+version\s+"([^"]+)"') +SKIPPED_DIRS = {".git", ".gradle", ".idea", "build", "node_modules", "target"} def coordinates(pom_texts): @@ -94,6 +104,38 @@ def compare(own, others): return warnings +def gradle_kotlin_plugins(root): + """(path, plugin id, version) for every Kotlin Gradle plugin a `*.gradle.kts` below root pins, in + path order; build output and the tools' own directories are not searched.""" + found = [] + for dirpath, dirnames, filenames in os.walk(root): + dirnames[:] = sorted(d for d in dirnames if d not in SKIPPED_DIRS) + for name in sorted(filenames): + if not name.endswith(".gradle.kts"): + continue + path = os.path.join(dirpath, name) + with open(path, encoding="utf-8") as f: + text = f.read() + for match in GRADLE_KOTLIN.finditer(text): + plugin = match.group(1) or f"{KOTLIN_GROUP}.{match.group(2)}" + found.append((os.path.relpath(path, root).replace(os.sep, "/"), plugin, match.group(3))) + return found + + +def compare_gradle_kotlin(own, plugins): + """Warnings for every Kotlin Gradle plugin whose version is not one the POMs use for + `org.jetbrains.kotlin:*`; nothing when the POMs use no Kotlin or no Gradle file pins a plugin.""" + maven = set() + for coordinate, used in own.items(): + if coordinate.startswith(KOTLIN_GROUP + ":"): + maven |= used + if not maven: + return [] + return [f"{plugin} is {version} in {path} and the POMs use Kotlin {', '.join(sorted(maven))}: " + f"Dependabot bumps only the POMs, bump the Gradle plugin with them" + for path, plugin, version in plugins if version not in maven] + + def fetch(repo, path): with urllib.request.urlopen(RAW_URL.format(owner=OWNER, repo=repo, path=path), timeout=20) as response: return response.read().decode("utf-8") @@ -129,7 +171,7 @@ def main(argv, root, fetcher=fetch, out=sys.stdout, err=sys.stderr): print(f"::warning::could not read {sibling}'s {path}: {e}", file=err) if texts: others[sibling] = coordinates(texts) - warnings = compare(own, others) + warnings = compare(own, others) + compare_gradle_kotlin(own, gradle_kotlin_plugins(root)) for warning in warnings: print(f"::warning::{warning}", file=err) shared = set(own).intersection(*others.values()) if others else set() diff --git a/.github/print-host-info.sh b/.github/print-host-info.sh new file mode 100755 index 00000000..5e9928c5 --- /dev/null +++ b/.github/print-host-info.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2026 Bernard Ladenthin +# +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Prints the runner's CPU, for reading a native build or test log: which instruction sets the +# compiler saw (GGML_NATIVE, the x86 variants) and how many cores the job had. Informational only +# -- it never fails the step. One script for every OS (the step runs `shell: bash`, which is Git +# Bash on Windows). +case "$(uname -s)" in + Linux) + echo "=== CPU information (lscpu) ===" + lscpu || true + echo + echo "=== CPU details (/proc/cpuinfo) ===" + cat /proc/cpuinfo || true + ;; + Darwin) + echo "=== CPU information (sysctl) ===" + sysctl hw.model hw.cachelinesize hw.cpufrequency hw.cachesize hw.physicalcpu hw.logicalcpu \ + hw.packages hw.memsize hw.ncpu 2>/dev/null || true + echo + echo "=== Processor details (system_profiler) ===" + system_profiler SPHardwareDataType || true + ;; + MINGW* | MSYS* | CYGWIN*) + echo "=== CPU information (Get-CimInstance Win32_Processor) ===" + powershell.exe -NoProfile -Command 'Get-CimInstance Win32_Processor | Select-Object * | Format-List' || true + echo "=== Processor (systeminfo) ===" + systeminfo | grep -i processor || true + ;; + *) + echo "no CPU report for $(uname -s)" + ;; +esac +exit 0 diff --git a/.github/shared-files.sha256 b/.github/shared-files.sha256 index b2585e74..e047d4e3 100644 --- a/.github/shared-files.sha256 +++ b/.github/shared-files.sha256 @@ -22,13 +22,14 @@ cbe504ad081ba31ec16e11c11f879c64179ca7741bf0b1d1318a7fc969ed67dd .github/buildc fe36042fbd5444f0b454ad1304cdafde901dd8cec57cfaba7c7086d996dfee27 .github/buildcheck/tests/test_sharedfiles.py 7fe208dd33184b4543328f97b339c95e23c408b120e6bfa6095de4182aeb9a19 .github/check-release-gate.py 1604ccdea83998814a3c1f98f746272222039867b0c7d692355e61495c1c648b .github/check-shared-files.py -e5f1679dc7151b2676b2f587860bbaa17e1b89b44a5a4e9703eb3499248c4a06 .github/buildcheck/versions.py -d454d33b61089b5f7bd6d439e3e5b58e01e7b01eeba6b4c2f7ed3360bbb158f0 .github/buildcheck/tests/test_versions.py +74b4b18af6c5be2af54048de408ec6e138f37f85b9f9ed2fdef258e270a7c90c .github/buildcheck/versions.py +de437064302f9314028afff00e17fc995e48869e07ee402206d23708c7ed355a .github/buildcheck/tests/test_versions.py 0c78f43dd181f079d35edde7cbf25bb22eac92c907845d8dc8eec78e812aa097 .github/check-versions.py 33c70847b6568604e8725d167a9b1d5910cc2a1a5b01fec7362be31b488ddb02 .github/buildcheck/runscripts.py c4967e3011d474ec43dbc797d113685c0a8a01b7cc52e910a1356273e6a67dc9 .github/buildcheck/tests/test_runscripts.py 7ad8f7258c983c91e67db695e5ab6ea0f9279af007a6aa815f92f0d8a74c2ae4 .github/check-run-scripts.py 7da53fdb9537ccf7d3f3740be31f295bacb914fe186304b9ed8a65fd3d3d413e .github/print-crash-logs.sh +fa1469664cb632e210ff3aa0669f15219d160ef13b486abeb978efade55f330b .github/print-host-info.sh c8c02dc39aeacc154fad74071443c6513666aa94ca9f9c5fab49c5f39dea7ee6 .github/verify-signing-key.sh e0b2a4f331eae326b61c71f78d6272d06d99140a85dedb7710f93402de862466 .github/verify-bytecode-version.sh ab45f5c102b47dd16c325d4d9c283d158ba90c05f484eac45b2767885c4462f9 .github/signing-selftest/build.gradle.kts diff --git a/.github/workflows/measurement-tools.yml b/.github/workflows/measurement-tools.yml index d4f0ba24..8c3887b0 100644 --- a/.github/workflows/measurement-tools.yml +++ b/.github/workflows/measurement-tools.yml @@ -50,4 +50,4 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.10', '3.14'] + python-version: ['3.10', '3.15'] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1eccc5b8..569f53b4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -265,6 +265,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v7 + - name: Display CPU Info + shell: bash + run: bash .github/print-host-info.sh - uses: actions/setup-java@v6 with: java-version-file: .java-version @@ -326,6 +329,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + - name: Display CPU Info + shell: bash + run: bash .github/print-host-info.sh - uses: actions/setup-java@v6 with: java-version-file: .java-version @@ -388,6 +394,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + - name: Display CPU Info + shell: bash + run: bash .github/print-host-info.sh - uses: actions/setup-java@v6 with: java-version-file: .java-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 0981b420..eb8fa924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed +- Build and test tooling bumped in step with the sibling repositories: Spotless 3.10.4, Checker + Framework 4.3.1, `commons-codec` 1.23.0; the measurement-tools matrix runs Python 3.10 and 3.15 + (3.15.0 final, from 3.14). The shared `check-versions.py` now also compares the Kotlin Gradle plugins + of a repository with its POMs' Kotlin (nothing to compare here) and reads java-llama.cpp's + `examples/server/pom.xml`. +- **CI prints the runner's CPU** at the top of `test`, `test-opencl` and `vmlens` + (`.github/print-host-info.sh`, a shared file of the four repositories): pocl computes on the CPU, so a + throughput or a timing in those logs can be read against the hardware the job drew. - **CI: snapshot deploy fails with HTTP 401 on Maven 3.10 (seen in java-llama.cpp).** The runners moved to Maven 3.10, which sends a ``'s credentials only to the origins declared for it; for the id `central` that is `https://repo.maven.apache.org`, so the upload to `central.sonatype.com/repository/maven-snapshots/` diff --git a/CLAUDE.md b/CLAUDE.md index b79fd287..c2c01e5b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -779,7 +779,9 @@ default branch lists it with a different hash. To change a shared file, change e publish jobs unless `.github/release-gate-exemptions.txt` names it with a reason, and **`check-versions.py`**, which **warns** where a Maven dependency or plugin (incl. the Spotless formatter version) is used here in another version than in a sibling repository -- Dependabot bumps -each repository on its own, so this is where the drift shows -- and **`check-run-scripts.py`**, which +each repository on its own, so this is where the drift shows; it also compares the Kotlin Gradle plugins +of a repository's `*.gradle.kts` files with its POMs' Kotlin, the files Dependabot never sees -- and +**`check-run-scripts.py`**, which runs `bash -n` over every `run:` script of the workflows and composite actions that runs in bash, so a broken script (a lost line continuation, say) fails here instead of in the job running it. The JDK every workflow uses is `.java-version` (setup-java's `java-version-file`), the same shared file in all four. Details and the diff --git a/pom.xml b/pom.xml index 8ad63f48..86c7f3a8 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ SPDX-License-Identifier: Apache-2.0 1.14.0 2.50.0 0.14.2 - 4.3.0 + 4.3.1 1.6.0 0.6.0 1.0.1 @@ -86,7 +86,7 @@ SPDX-License-Identifier: Apache-2.0 0.9.3 3.4.3 2.22.0 - 1.22.1 + 1.23.0 3.10.0 6.1.3 3.0 @@ -106,7 +106,7 @@ SPDX-License-Identifier: Apache-2.0 artifact reaches a published artifact at all; both are test scope. --> 2.26.1 1.37 - 3.10.3 + 3.10.4 2.102.0