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
41 changes: 41 additions & 0 deletions .github/buildcheck/tests/test_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import io
import os
import tempfile
import unittest
from unittest import mock

Expand Down Expand Up @@ -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):
Expand All @@ -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('<dependencies><dependency><groupId>org.jetbrains.kotlin</groupId>'
'<artifactId>kotlin-stdlib</artifactId><version>2.4.21</version></dependency></dependencies>')
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('<dependencies><dependency><groupId>org.junit.jupiter</groupId>'
Expand Down
46 changes: 44 additions & 2 deletions .github/buildcheck/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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",),
Expand All @@ -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):
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down
37 changes: 37 additions & 0 deletions .github/print-host-info.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
#
# 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
5 changes: 3 additions & 2 deletions .github/shared-files.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/measurement-tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.14']
python-version: ['3.10', '3.15']
9 changes: 9 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<server>`'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/`
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ SPDX-License-Identifier: Apache-2.0
<findsecbugs.version>1.14.0</findsecbugs.version>
<errorprone.version>2.50.0</errorprone.version>
<nullaway.version>0.14.2</nullaway.version>
<checker.version>4.3.0</checker.version>
<checker.version>4.3.1</checker.version>
<java-websocket.version>1.6.0</java-websocket.version>
<jeromq.version>0.6.0</jeromq.version>
<jspecify.version>1.0.1</jspecify.version>
Expand All @@ -86,7 +86,7 @@ SPDX-License-Identifier: Apache-2.0
<lmdbjava.version>0.9.3</lmdbjava.version>
<lwjgl.version>3.4.3</lwjgl.version>
<commons-io.version>2.22.0</commons-io.version>
<commons-codec.version>1.22.1</commons-codec.version>
<commons-codec.version>1.23.0</commons-codec.version>
<maven-artifact.version>3.10.0</maven-artifact.version>
<junit.version>6.1.3</junit.version>
<hamcrest.version>3.0</hamcrest.version>
Expand All @@ -106,7 +106,7 @@ SPDX-License-Identifier: Apache-2.0
artifact reaches a published artifact at all; both are test scope. -->
<log4j.version>2.26.1</log4j.version>
<jmh.version>1.37</jmh.version>
<spotless.version>3.10.3</spotless.version>
<spotless.version>3.10.4</spotless.version>
<palantir-java-format.version>2.102.0</palantir-java-format.version>
</properties>

Expand Down
Loading