From 0eea3c1e95e473f900e6176440f4d2f815c160f6 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 5 May 2026 15:03:15 +0000 Subject: [PATCH 1/8] fix: restore legacy suffix compatibility Signed-off-by: Gregor Zeitlinger --- .../PrometheusProtobufWriterImpl.java | 51 +++++++++++++---- .../DuplicateNamesProtobufTest.java | 22 +++++++ .../PrometheusTextFormatWriter.java | 18 +++++- .../ExpositionFormatsTest.java | 57 +++++++++++++++++++ 4 files changed, 135 insertions(+), 13 deletions(-) diff --git a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java index 5e58275ca..6dd0bbd31 100644 --- a/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java +++ b/prometheus-metrics-exposition-formats/src/main/java/io/prometheus/metrics/expositionformats/internal/PrometheusProtobufWriterImpl.java @@ -21,6 +21,7 @@ import io.prometheus.metrics.model.snapshots.MetricSnapshot; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import io.prometheus.metrics.model.snapshots.NativeHistogramBuckets; +import io.prometheus.metrics.model.snapshots.PrometheusNaming; import io.prometheus.metrics.model.snapshots.Quantiles; import io.prometheus.metrics.model.snapshots.SnapshotEscaper; import io.prometheus.metrics.model.snapshots.StateSetSnapshot; @@ -82,7 +83,7 @@ public Metrics.MetricFamily convert(MetricSnapshot snapshot, EscapingScheme sche builder.addMetric(convert(data, scheme)); } setMetadataUnlessEmpty( - builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme); + builder, snapshot.getMetadata(), null, Metrics.MetricType.GAUGE, scheme, true); } else if (snapshot instanceof HistogramSnapshot) { HistogramSnapshot histogram = (HistogramSnapshot) snapshot; for (HistogramSnapshot.HistogramDataPointSnapshot data : histogram.getDataPoints()) { @@ -290,25 +291,53 @@ private void setMetadataUnlessEmpty( @Nullable String nameSuffix, Metrics.MetricType type, EscapingScheme scheme) { + setMetadataUnlessEmpty(builder, metadata, nameSuffix, type, scheme, false); + } + + private void setMetadataUnlessEmpty( + Metrics.MetricFamily.Builder builder, + MetricMetadata metadata, + @Nullable String nameSuffix, + Metrics.MetricType type, + EscapingScheme scheme, + boolean normalizeLegacyGaugeName) { if (builder.getMetricCount() == 0) { return; } - if (nameSuffix == null) { - builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme)); - } else { - String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme); - if (expositionBaseName.endsWith(nameSuffix)) { - builder.setName(expositionBaseName); - } else { - builder.setName(SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix); - } - } + builder.setName( + resolveMetricFamilyName(metadata, nameSuffix, scheme, normalizeLegacyGaugeName)); if (metadata.getHelp() != null) { builder.setHelp(metadata.getHelp()); } builder.setType(type); } + private String resolveMetricFamilyName( + MetricMetadata metadata, + @Nullable String nameSuffix, + EscapingScheme scheme, + boolean normalizeLegacyGaugeName) { + if (normalizeLegacyGaugeName) { + String originalName = metadata.getOriginalName(); + if (originalName.endsWith(".created")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".created".length()), scheme); + } + if (originalName.endsWith(".total")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".total".length()), scheme); + } + } + if (nameSuffix == null) { + return SnapshotEscaper.getMetadataName(metadata, scheme); + } + String expositionBaseName = SnapshotEscaper.getExpositionBaseMetadataName(metadata, scheme); + if (expositionBaseName.endsWith(nameSuffix)) { + return expositionBaseName; + } + return SnapshotEscaper.getMetadataName(metadata, scheme) + nameSuffix; + } + private long getNativeCount(HistogramSnapshot.HistogramDataPointSnapshot data) { if (data.hasCount()) { return data.getCount(); diff --git a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java index c5fc7bf34..ba0b653c0 100644 --- a/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java +++ b/prometheus-metrics-exposition-formats/src/test/java/io/prometheus/metrics/expositionformats/DuplicateNamesProtobufTest.java @@ -239,6 +239,28 @@ void testDifferentMetrics_producesSeparateMetricFamilies() throws IOException { assertThat(gaugeFamily.getMetric(0).getGauge().getValue()).isEqualTo(50.0); } + @Test + void testLegacyGaugeNameWithDotTotal_usesBaseName() throws IOException { + MetricSnapshots snapshots = + MetricSnapshots.of( + GaugeSnapshot.builder() + .name("legacy.total") + .dataPoint(GaugeSnapshot.GaugeDataPointSnapshot.builder().value(7).build()) + .build()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrometheusProtobufWriterImpl writer = new PrometheusProtobufWriterImpl(); + writer.write(out, snapshots, EscapingScheme.UNDERSCORE_ESCAPING); + + List metricFamilies = parseProtobufOutput(out); + + assertThat(metricFamilies).hasSize(1); + Metrics.MetricFamily family = metricFamilies.get(0); + assertThat(family.getName()).isEqualTo("legacy"); + assertThat(family.getType()).isEqualTo(Metrics.MetricType.GAUGE); + assertThat(family.getMetricCount()).isEqualTo(1); + assertThat(family.getMetric(0).getGauge().getValue()).isEqualTo(7.0); + } + private static MetricSnapshots getMetricSnapshots() { PrometheusRegistry registry = new PrometheusRegistry(); diff --git a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java index b40dcfdf2..af4300c94 100644 --- a/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java +++ b/prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/PrometheusTextFormatWriter.java @@ -192,9 +192,10 @@ private void writeCounter(Writer writer, CounterSnapshot snapshot, EscapingSchem private void writeGauge(Writer writer, GaugeSnapshot snapshot, EscapingScheme scheme) throws IOException { MetricMetadata metadata = snapshot.getMetadata(); - writeMetadata(writer, "", "gauge", metadata, scheme); + String gaugeName = resolveLegacyGaugeName(metadata, scheme); + writeMetadataWithFullName(writer, gaugeName, "gauge", metadata); for (GaugeSnapshot.GaugeDataPointSnapshot data : snapshot.getDataPoints()) { - writeNameAndLabels(writer, getMetadataName(metadata, scheme), null, data.getLabels(), scheme); + writeNameAndLabels(writer, gaugeName, null, data.getLabels(), scheme); writeDouble(writer, data.getValue()); writeScrapeTimestampAndNewline(writer, data); } @@ -475,6 +476,19 @@ private static String resolveBaseName(String fullName, String suffix) { return fullName; } + private static String resolveLegacyGaugeName(MetricMetadata metadata, EscapingScheme scheme) { + String originalName = metadata.getOriginalName(); + if (originalName.endsWith(".created")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".created".length()), scheme); + } + if (originalName.endsWith(".total")) { + return PrometheusNaming.escapeName( + originalName.substring(0, originalName.length() - ".total".length()), scheme); + } + return getMetadataName(metadata, scheme); + } + private void writeEscapedHelp(Writer writer, String s) throws IOException { for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); diff --git a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java index 454b3ef7b..3deeb6b37 100644 --- a/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java +++ b/prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/ExpositionFormatsTest.java @@ -666,6 +666,63 @@ void testGaugeWithDots() throws IOException { assertPrometheusProtobuf(prometheusProtobuf, gauge); } + @Test + void testGaugeReservedSuffixCompatibilityOutsideOpenMetrics() throws IOException { + GaugeSnapshot createdGauge = + GaugeSnapshot.builder() + .name("test3.created") + .dataPoint(GaugeDataPointSnapshot.builder().value(3).build()) + .build(); + assertOpenMetricsText( + """ + # TYPE U__test3_2e_created gauge + U__test3_2e_created 3.0 + # EOF + """, + createdGauge); + assertPrometheusText( + """ + # TYPE test3 gauge + test3 3.0 + """, + createdGauge); + assertPrometheusTextWithoutCreated( + """ + # TYPE test3 gauge + test3 3.0 + """, + createdGauge); + assertPrometheusProtobuf( + "name: \"test3\" type: GAUGE metric { gauge { value: 3.0 } }", createdGauge); + + GaugeSnapshot totalGauge = + GaugeSnapshot.builder() + .name("test6.total") + .dataPoint(GaugeDataPointSnapshot.builder().value(6).build()) + .build(); + assertOpenMetricsText( + """ + # TYPE U__test6_2e_total gauge + U__test6_2e_total 6.0 + # EOF + """, + totalGauge); + assertPrometheusText( + """ + # TYPE test6 gauge + test6 6.0 + """, + totalGauge); + assertPrometheusTextWithoutCreated( + """ + # TYPE test6 gauge + test6 6.0 + """, + totalGauge); + assertPrometheusProtobuf( + "name: \"test6\" type: GAUGE metric { gauge { value: 6.0 } }", totalGauge); + } + @Test void testGaugeUTF8() throws IOException { String prometheusText = From 7bde1c24a28be0c21c9219abf05ffab895bfdf8e Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 4 May 2026 11:22:08 +0000 Subject: [PATCH 2/8] feat: add micrometer compatibility workflow Signed-off-by: Gregor Zeitlinger --- .../workflows/micrometer-compatibility.yml | 36 ++++++ .mise/lib/micrometer_compat.py | 115 ++++++++++++++++++ .mise/tasks/micrometer/prepare.py | 25 ++++ .mise/tasks/micrometer/test-class.py | 31 +++++ .mise/tasks/micrometer/test.py | 31 +++++ 5 files changed, 238 insertions(+) create mode 100644 .github/workflows/micrometer-compatibility.yml create mode 100644 .mise/lib/micrometer_compat.py create mode 100755 .mise/tasks/micrometer/prepare.py create mode 100755 .mise/tasks/micrometer/test-class.py create mode 100755 .mise/tasks/micrometer/test.py diff --git a/.github/workflows/micrometer-compatibility.yml b/.github/workflows/micrometer-compatibility.yml new file mode 100644 index 000000000..6cab6f9c8 --- /dev/null +++ b/.github/workflows/micrometer-compatibility.yml @@ -0,0 +1,36 @@ +--- +name: Micrometer Compatibility + +on: + pull_request: + workflow_dispatch: + inputs: + micrometer-ref: + description: Micrometer branch, tag, or commit to test + required: false + default: main + +permissions: {} + +jobs: + micrometer-compatibility: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 + with: + version: v2026.4.23 + sha256: 4a650daf1c6db2bb9c32a4d4f6d2389051906f85792d97b04ad10b9f6e212372 + - name: Cache local Maven repository + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Run Micrometer compatibility tests + env: + MICROMETER_REF: ${{ github.event.inputs.micrometer-ref || 'main' }} + run: mise run micrometer:test diff --git a/.mise/lib/micrometer_compat.py b/.mise/lib/micrometer_compat.py new file mode 100644 index 000000000..1205522bf --- /dev/null +++ b/.mise/lib/micrometer_compat.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import os +import subprocess +from pathlib import Path +from typing import Optional + + +ROOT_DIR = Path(__file__).resolve().parents[2] +DEFAULT_MICROMETER_DIR = Path(os.environ.get("MICROMETER_DIR", "/tmp/micrometer-compat")) +DEFAULT_MICROMETER_REMOTE = os.environ.get("MICROMETER_REMOTE", "origin") +DEFAULT_MICROMETER_REF = os.environ.get("MICROMETER_REF", "main") +DEFAULT_INIT_SCRIPT = Path( + os.environ.get("MICROMETER_INIT_SCRIPT", "/tmp/micrometer-prom-local.init.gradle") +) +DEFAULT_PROM_VERSION = os.environ.get("PROM_VERSION", "1.6.1") + + +def run_cmd(cmd: list[str], cwd: Optional[Path] = None) -> None: + subprocess.run(cmd, cwd=cwd, check=True) + + +def check_clean_worktree(micrometer_dir: Path) -> None: + result = subprocess.run( + ["git", "status", "--short"], + cwd=micrometer_dir, + check=True, + capture_output=True, + text=True, + ) + if result.stdout.strip(): + raise RuntimeError( + f"{micrometer_dir} has uncommitted changes; use a clean clone or set MICROMETER_DIR" + ) + + +def write_init_script( + init_script: Path = DEFAULT_INIT_SCRIPT, prom_version: str = DEFAULT_PROM_VERSION +) -> None: + init_script.write_text( + f"""allprojects {{ + repositories {{ + mavenLocal() + mavenCentral() + gradlePluginPortal() + }} + configurations.configureEach {{ + resolutionStrategy.eachDependency {{ details -> + if (details.requested.group == 'io.prometheus') {{ + details.useVersion('{prom_version}') + details.because('Use local prom_client_java artifacts for downstream compatibility testing') + }} + }} + }} +}} +""", + encoding="utf-8", + ) + + +def prepare_repo( + micrometer_dir: Path = DEFAULT_MICROMETER_DIR, + remote: str = DEFAULT_MICROMETER_REMOTE, + ref: str = DEFAULT_MICROMETER_REF, +) -> None: + if (micrometer_dir / ".git").is_dir(): + check_clean_worktree(micrometer_dir) + run_cmd(["git", "fetch", remote, ref], cwd=micrometer_dir) + else: + run_cmd( + [ + "git", + "clone", + "https://github.com/micrometer-metrics/micrometer.git", + str(micrometer_dir), + ] + ) + run_cmd(["git", "fetch", remote, ref], cwd=micrometer_dir) + run_cmd( + ["git", "checkout", "-B", "codex-micrometer-compat", "FETCH_HEAD"], + cwd=micrometer_dir, + ) + + +def install_local_artifacts(root_dir: Path = ROOT_DIR) -> None: + run_cmd( + [ + "./mvnw", + "install", + "-DskipTests", + "-Dcoverage.skip=true", + "-Dcheckstyle.skip=true", + "-Dwarnings=-nowarn", + ], + cwd=root_dir, + ) + + +def run_gradle_test( + test_selector: Optional[str] = None, + micrometer_dir: Path = DEFAULT_MICROMETER_DIR, + init_script: Path = DEFAULT_INIT_SCRIPT, +) -> None: + cmd = [ + "./gradlew", + "--no-daemon", + "-I", + str(init_script), + ":micrometer-registry-prometheus:test", + ] + if test_selector: + cmd.extend(["--tests", test_selector]) + run_cmd(cmd, cwd=micrometer_dir) diff --git a/.mise/tasks/micrometer/prepare.py b/.mise/tasks/micrometer/prepare.py new file mode 100755 index 000000000..3d6eb9227 --- /dev/null +++ b/.mise/tasks/micrometer/prepare.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +# [MISE] description="Install local artifacts and check out a target Micrometer ref" +# [MISE] alias="micrometer:prepare" + +from pathlib import Path +import sys + + +ROOT = Path(__file__).resolve().parents[2] +if str(ROOT / "lib") not in sys.path: + sys.path.insert(0, str(ROOT / "lib")) + +from micrometer_compat import install_local_artifacts, prepare_repo, write_init_script + + +def main() -> int: + install_local_artifacts() + prepare_repo() + write_init_script() + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.mise/tasks/micrometer/test-class.py b/.mise/tasks/micrometer/test-class.py new file mode 100755 index 000000000..8cc54477c --- /dev/null +++ b/.mise/tasks/micrometer/test-class.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# [MISE] description="Run Micrometer PrometheusMeterRegistryTest against a target Micrometer ref" +# [MISE] alias="micrometer:test-class" + +from pathlib import Path +import sys + + +ROOT = Path(__file__).resolve().parents[2] +if str(ROOT / "lib") not in sys.path: + sys.path.insert(0, str(ROOT / "lib")) + +from micrometer_compat import ( + install_local_artifacts, + prepare_repo, + run_gradle_test, + write_init_script, +) + + +def main() -> int: + install_local_artifacts() + prepare_repo() + write_init_script() + run_gradle_test("io.micrometer.prometheusmetrics.PrometheusMeterRegistryTest") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.mise/tasks/micrometer/test.py b/.mise/tasks/micrometer/test.py new file mode 100755 index 000000000..3672cdd13 --- /dev/null +++ b/.mise/tasks/micrometer/test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# [MISE] description="Run Micrometer Prometheus registry tests against a target Micrometer ref" +# [MISE] alias="micrometer:test" + +from pathlib import Path +import sys + + +ROOT = Path(__file__).resolve().parents[2] +if str(ROOT / "lib") not in sys.path: + sys.path.insert(0, str(ROOT / "lib")) + +from micrometer_compat import ( + install_local_artifacts, + prepare_repo, + run_gradle_test, + write_init_script, +) + + +def main() -> int: + install_local_artifacts() + prepare_repo() + write_init_script() + run_gradle_test() + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 6693f4d4d7d4602c8011186c33341c9040f57267 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 4 May 2026 11:24:18 +0000 Subject: [PATCH 3/8] fix: clean up micrometer task tooling Signed-off-by: Gregor Zeitlinger --- .github/renovate-tracked-deps.json | 5 +++++ .mise/lib/micrometer_compat.py | 4 +++- .mise/tasks/micrometer/prepare.py | 8 ++++++-- .mise/tasks/micrometer/test-class.py | 14 +++++++------- .mise/tasks/micrometer/test.py | 14 +++++++------- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.github/renovate-tracked-deps.json b/.github/renovate-tracked-deps.json index b29b53789..d101f7abf 100644 --- a/.github/renovate-tracked-deps.json +++ b/.github/renovate-tracked-deps.json @@ -34,6 +34,11 @@ "mise" ] }, + ".github/workflows/micrometer-compatibility.yml": { + "regex": [ + "mise" + ] + }, ".github/workflows/native-tests.yml": { "regex": [ "mise" diff --git a/.mise/lib/micrometer_compat.py b/.mise/lib/micrometer_compat.py index 1205522bf..29e96a1aa 100644 --- a/.mise/lib/micrometer_compat.py +++ b/.mise/lib/micrometer_compat.py @@ -9,7 +9,9 @@ ROOT_DIR = Path(__file__).resolve().parents[2] -DEFAULT_MICROMETER_DIR = Path(os.environ.get("MICROMETER_DIR", "/tmp/micrometer-compat")) +DEFAULT_MICROMETER_DIR = Path( + os.environ.get("MICROMETER_DIR", "/tmp/micrometer-compat") +) DEFAULT_MICROMETER_REMOTE = os.environ.get("MICROMETER_REMOTE", "origin") DEFAULT_MICROMETER_REF = os.environ.get("MICROMETER_REF", "main") DEFAULT_INIT_SCRIPT = Path( diff --git a/.mise/tasks/micrometer/prepare.py b/.mise/tasks/micrometer/prepare.py index 3d6eb9227..772f51368 100755 --- a/.mise/tasks/micrometer/prepare.py +++ b/.mise/tasks/micrometer/prepare.py @@ -11,10 +11,14 @@ if str(ROOT / "lib") not in sys.path: sys.path.insert(0, str(ROOT / "lib")) -from micrometer_compat import install_local_artifacts, prepare_repo, write_init_script - def main() -> int: + from micrometer_compat import ( + install_local_artifacts, + prepare_repo, + write_init_script, + ) + install_local_artifacts() prepare_repo() write_init_script() diff --git a/.mise/tasks/micrometer/test-class.py b/.mise/tasks/micrometer/test-class.py index 8cc54477c..e87c88bb3 100755 --- a/.mise/tasks/micrometer/test-class.py +++ b/.mise/tasks/micrometer/test-class.py @@ -11,15 +11,15 @@ if str(ROOT / "lib") not in sys.path: sys.path.insert(0, str(ROOT / "lib")) -from micrometer_compat import ( - install_local_artifacts, - prepare_repo, - run_gradle_test, - write_init_script, -) - def main() -> int: + from micrometer_compat import ( + install_local_artifacts, + prepare_repo, + run_gradle_test, + write_init_script, + ) + install_local_artifacts() prepare_repo() write_init_script() diff --git a/.mise/tasks/micrometer/test.py b/.mise/tasks/micrometer/test.py index 3672cdd13..790ce3171 100755 --- a/.mise/tasks/micrometer/test.py +++ b/.mise/tasks/micrometer/test.py @@ -11,15 +11,15 @@ if str(ROOT / "lib") not in sys.path: sys.path.insert(0, str(ROOT / "lib")) -from micrometer_compat import ( - install_local_artifacts, - prepare_repo, - run_gradle_test, - write_init_script, -) - def main() -> int: + from micrometer_compat import ( + install_local_artifacts, + prepare_repo, + run_gradle_test, + write_init_script, + ) + install_local_artifacts() prepare_repo() write_init_script() From f36163b397c157fdb22a12d88ab41f11b9747625 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 4 May 2026 11:42:42 +0000 Subject: [PATCH 4/8] fix: allow micrometer fork compatibility testing Signed-off-by: Gregor Zeitlinger --- .github/workflows/micrometer-compatibility.yml | 5 +++++ .mise/lib/micrometer_compat.py | 17 ++++++++++++++--- .mise/tasks/micrometer/prepare.py | 5 +---- .mise/tasks/micrometer/test-class.py | 5 +---- .mise/tasks/micrometer/test.py | 5 +---- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/micrometer-compatibility.yml b/.github/workflows/micrometer-compatibility.yml index 6cab6f9c8..162dc6be0 100644 --- a/.github/workflows/micrometer-compatibility.yml +++ b/.github/workflows/micrometer-compatibility.yml @@ -5,6 +5,10 @@ on: pull_request: workflow_dispatch: inputs: + micrometer-repository: + description: Micrometer repository to test, in owner/name form + required: false + default: micrometer-metrics/micrometer micrometer-ref: description: Micrometer branch, tag, or commit to test required: false @@ -32,5 +36,6 @@ jobs: ${{ runner.os }}-maven- - name: Run Micrometer compatibility tests env: + MICROMETER_REPOSITORY: ${{ github.event.inputs.micrometer-repository || 'micrometer-metrics/micrometer' }} MICROMETER_REF: ${{ github.event.inputs.micrometer-ref || 'main' }} run: mise run micrometer:test diff --git a/.mise/lib/micrometer_compat.py b/.mise/lib/micrometer_compat.py index 29e96a1aa..4e0540a25 100644 --- a/.mise/lib/micrometer_compat.py +++ b/.mise/lib/micrometer_compat.py @@ -8,10 +8,12 @@ from typing import Optional -ROOT_DIR = Path(__file__).resolve().parents[2] DEFAULT_MICROMETER_DIR = Path( os.environ.get("MICROMETER_DIR", "/tmp/micrometer-compat") ) +DEFAULT_MICROMETER_REPOSITORY = os.environ.get( + "MICROMETER_REPOSITORY", "micrometer-metrics/micrometer" +) DEFAULT_MICROMETER_REMOTE = os.environ.get("MICROMETER_REMOTE", "origin") DEFAULT_MICROMETER_REF = os.environ.get("MICROMETER_REF", "main") DEFAULT_INIT_SCRIPT = Path( @@ -24,6 +26,10 @@ def run_cmd(cmd: list[str], cwd: Optional[Path] = None) -> None: subprocess.run(cmd, cwd=cwd, check=True) +def micrometer_repository_url(repository: str) -> str: + return f"https://github.com/{repository}.git" + + def check_clean_worktree(micrometer_dir: Path) -> None: result = subprocess.run( ["git", "status", "--short"], @@ -64,18 +70,23 @@ def write_init_script( def prepare_repo( micrometer_dir: Path = DEFAULT_MICROMETER_DIR, + repository: str = DEFAULT_MICROMETER_REPOSITORY, remote: str = DEFAULT_MICROMETER_REMOTE, ref: str = DEFAULT_MICROMETER_REF, ) -> None: + repository_url = micrometer_repository_url(repository) if (micrometer_dir / ".git").is_dir(): check_clean_worktree(micrometer_dir) + run_cmd( + ["git", "remote", "set-url", remote, repository_url], cwd=micrometer_dir + ) run_cmd(["git", "fetch", remote, ref], cwd=micrometer_dir) else: run_cmd( [ "git", "clone", - "https://github.com/micrometer-metrics/micrometer.git", + repository_url, str(micrometer_dir), ] ) @@ -86,7 +97,7 @@ def prepare_repo( ) -def install_local_artifacts(root_dir: Path = ROOT_DIR) -> None: +def install_local_artifacts(root_dir: Path = Path.cwd()) -> None: run_cmd( [ "./mvnw", diff --git a/.mise/tasks/micrometer/prepare.py b/.mise/tasks/micrometer/prepare.py index 772f51368..ba13acf69 100755 --- a/.mise/tasks/micrometer/prepare.py +++ b/.mise/tasks/micrometer/prepare.py @@ -3,13 +3,10 @@ # [MISE] description="Install local artifacts and check out a target Micrometer ref" # [MISE] alias="micrometer:prepare" -from pathlib import Path import sys -ROOT = Path(__file__).resolve().parents[2] -if str(ROOT / "lib") not in sys.path: - sys.path.insert(0, str(ROOT / "lib")) +sys.path.insert(0, ".mise/lib") def main() -> int: diff --git a/.mise/tasks/micrometer/test-class.py b/.mise/tasks/micrometer/test-class.py index e87c88bb3..89ae436de 100755 --- a/.mise/tasks/micrometer/test-class.py +++ b/.mise/tasks/micrometer/test-class.py @@ -3,13 +3,10 @@ # [MISE] description="Run Micrometer PrometheusMeterRegistryTest against a target Micrometer ref" # [MISE] alias="micrometer:test-class" -from pathlib import Path import sys -ROOT = Path(__file__).resolve().parents[2] -if str(ROOT / "lib") not in sys.path: - sys.path.insert(0, str(ROOT / "lib")) +sys.path.insert(0, ".mise/lib") def main() -> int: diff --git a/.mise/tasks/micrometer/test.py b/.mise/tasks/micrometer/test.py index 790ce3171..40316f569 100755 --- a/.mise/tasks/micrometer/test.py +++ b/.mise/tasks/micrometer/test.py @@ -3,13 +3,10 @@ # [MISE] description="Run Micrometer Prometheus registry tests against a target Micrometer ref" # [MISE] alias="micrometer:test" -from pathlib import Path import sys -ROOT = Path(__file__).resolve().parents[2] -if str(ROOT / "lib") not in sys.path: - sys.path.insert(0, str(ROOT / "lib")) +sys.path.insert(0, ".mise/lib") def main() -> int: From 200dad82d8340958ef27fa82a435986b5abd191d Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 4 May 2026 11:44:16 +0000 Subject: [PATCH 5/8] test: point micrometer compatibility at fork branch Signed-off-by: Gregor Zeitlinger --- .github/workflows/micrometer-compatibility.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/micrometer-compatibility.yml b/.github/workflows/micrometer-compatibility.yml index 162dc6be0..ef46b48db 100644 --- a/.github/workflows/micrometer-compatibility.yml +++ b/.github/workflows/micrometer-compatibility.yml @@ -36,6 +36,6 @@ jobs: ${{ runner.os }}-maven- - name: Run Micrometer compatibility tests env: - MICROMETER_REPOSITORY: ${{ github.event.inputs.micrometer-repository || 'micrometer-metrics/micrometer' }} - MICROMETER_REF: ${{ github.event.inputs.micrometer-ref || 'main' }} + MICROMETER_REPOSITORY: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.micrometer-repository || 'zeitlinger/micrometer' }} + MICROMETER_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.micrometer-ref || 'prom-client-java-compat-prototype' }} run: mise run micrometer:test From d5d3f1c0e02761d5cd1cb543be36c6dec08c9d53 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 4 May 2026 12:08:29 +0000 Subject: [PATCH 6/8] test: move micrometer compatibility defaults to mise Signed-off-by: Gregor Zeitlinger --- .github/workflows/micrometer-compatibility.yml | 10 ++++++---- mise.toml | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/micrometer-compatibility.yml b/.github/workflows/micrometer-compatibility.yml index ef46b48db..e044d5b2a 100644 --- a/.github/workflows/micrometer-compatibility.yml +++ b/.github/workflows/micrometer-compatibility.yml @@ -35,7 +35,9 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - name: Run Micrometer compatibility tests - env: - MICROMETER_REPOSITORY: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.micrometer-repository || 'zeitlinger/micrometer' }} - MICROMETER_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.micrometer-ref || 'prom-client-java-compat-prototype' }} - run: mise run micrometer:test + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + export MICROMETER_REPOSITORY="${{ github.event.inputs.micrometer-repository }}" + export MICROMETER_REF="${{ github.event.inputs.micrometer-ref }}" + fi + mise run micrometer:test diff --git a/mise.toml b/mise.toml index a6ac6c257..b320f024a 100644 --- a/mise.toml +++ b/mise.toml @@ -24,6 +24,8 @@ taplo = "0.10.0" [env] FLINT_CONFIG_DIR = ".github/config" +MICROMETER_REPOSITORY = "zeitlinger/micrometer" +MICROMETER_REF = "prom-client-java-compat-prototype" # renovate: datasource=github-releases depName=grafana/docker-otel-lgtm LGTM_VERSION = "0.25.0" From e19fce4c13d9e988b41b0a4cf8ac47374b38f044 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 5 May 2026 08:53:45 +0000 Subject: [PATCH 7/8] fix: pin micrometer compatibility target Signed-off-by: Gregor Zeitlinger --- mise.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.toml b/mise.toml index b320f024a..b55b8776e 100644 --- a/mise.toml +++ b/mise.toml @@ -25,7 +25,7 @@ taplo = "0.10.0" [env] FLINT_CONFIG_DIR = ".github/config" MICROMETER_REPOSITORY = "zeitlinger/micrometer" -MICROMETER_REF = "prom-client-java-compat-prototype" +MICROMETER_REF = "bea3c3badfaa12f302a5dbbe4ad68cd60ec9d419" # renovate: datasource=github-releases depName=grafana/docker-otel-lgtm LGTM_VERSION = "0.25.0" From eb0af9a46c38dad1c7c4086742a500af6dd74e88 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Tue, 5 May 2026 09:03:48 +0000 Subject: [PATCH 8/8] fix: derive micrometer test version from pom Signed-off-by: Gregor Zeitlinger --- .mise/lib/micrometer_compat.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.mise/lib/micrometer_compat.py b/.mise/lib/micrometer_compat.py index 4e0540a25..74bbb69a2 100644 --- a/.mise/lib/micrometer_compat.py +++ b/.mise/lib/micrometer_compat.py @@ -6,6 +6,7 @@ import subprocess from pathlib import Path from typing import Optional +import xml.etree.ElementTree as ET DEFAULT_MICROMETER_DIR = Path( @@ -19,7 +20,7 @@ DEFAULT_INIT_SCRIPT = Path( os.environ.get("MICROMETER_INIT_SCRIPT", "/tmp/micrometer-prom-local.init.gradle") ) -DEFAULT_PROM_VERSION = os.environ.get("PROM_VERSION", "1.6.1") +DEFAULT_PROM_VERSION = os.environ.get("PROM_VERSION") def run_cmd(cmd: list[str], cwd: Optional[Path] = None) -> None: @@ -44,9 +45,25 @@ def check_clean_worktree(micrometer_dir: Path) -> None: ) +def get_prom_version(root_dir: Path = Path.cwd()) -> str: + configured_version = DEFAULT_PROM_VERSION + if configured_version: + return configured_version + pom = ET.parse(root_dir / "pom.xml") + root = pom.getroot() + version = root.findtext("./{*}version") + if not version: + version = root.findtext("./{*}parent/{*}version") + if not version: + raise RuntimeError("could not determine Prometheus version from pom.xml") + return version + + def write_init_script( - init_script: Path = DEFAULT_INIT_SCRIPT, prom_version: str = DEFAULT_PROM_VERSION + init_script: Path = DEFAULT_INIT_SCRIPT, prom_version: Optional[str] = None ) -> None: + if prom_version is None: + prom_version = get_prom_version() init_script.write_text( f"""allprojects {{ repositories {{