Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .github/renovate-tracked-deps.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
"mise"
]
},
".github/workflows/micrometer-compatibility.yml": {
"regex": [
"mise"
]
},
".github/workflows/native-tests.yml": {
"regex": [
"mise"
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/micrometer-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: Micrometer Compatibility

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
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
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
145 changes: 145 additions & 0 deletions .mise/lib/micrometer_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env python3

from __future__ import annotations

import os
import subprocess
from pathlib import Path
from typing import Optional
import xml.etree.ElementTree as ET


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(
os.environ.get("MICROMETER_INIT_SCRIPT", "/tmp/micrometer-prom-local.init.gradle")
)
DEFAULT_PROM_VERSION = os.environ.get("PROM_VERSION")


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"],
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 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: Optional[str] = None
) -> None:
if prom_version is None:
prom_version = get_prom_version()
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,
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",
repository_url,
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 = Path.cwd()) -> 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)
26 changes: 26 additions & 0 deletions .mise/tasks/micrometer/prepare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3

# [MISE] description="Install local artifacts and check out a target Micrometer ref"
# [MISE] alias="micrometer:prepare"

import sys


sys.path.insert(0, ".mise/lib")


def main() -> int:
from micrometer_compat import (
install_local_artifacts,
prepare_repo,
write_init_script,
)

install_local_artifacts()
prepare_repo()
write_init_script()
return 0


if __name__ == "__main__":
raise SystemExit(main())
28 changes: 28 additions & 0 deletions .mise/tasks/micrometer/test-class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3

# [MISE] description="Run Micrometer PrometheusMeterRegistryTest against a target Micrometer ref"
# [MISE] alias="micrometer:test-class"

import sys


sys.path.insert(0, ".mise/lib")


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()
run_gradle_test("io.micrometer.prometheusmetrics.PrometheusMeterRegistryTest")
return 0


if __name__ == "__main__":
raise SystemExit(main())
28 changes: 28 additions & 0 deletions .mise/tasks/micrometer/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3

# [MISE] description="Run Micrometer Prometheus registry tests against a target Micrometer ref"
# [MISE] alias="micrometer:test"

import sys


sys.path.insert(0, ".mise/lib")


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()
run_gradle_test()
return 0


if __name__ == "__main__":
raise SystemExit(main())
2 changes: 2 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ taplo = "0.10.0"

[env]
FLINT_CONFIG_DIR = ".github/config"
MICROMETER_REPOSITORY = "zeitlinger/micrometer"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

don't merge that!

MICROMETER_REF = "bea3c3badfaa12f302a5dbbe4ad68cd60ec9d419"
# renovate: datasource=github-releases depName=grafana/docker-otel-lgtm
LGTM_VERSION = "0.25.0"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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();
Expand Down
Loading