Skip to content

Commit 0429d64

Browse files
committed
Merge remote-tracking branch 'upstream/main' into initializer
# Conflicts: # instrumentation/spring/spring-web/spring-web-3.1/library/src/main/java/io/opentelemetry/instrumentation/spring/web/v3_1/internal/WebTelemetryUtil.java # instrumentation/spring/spring-webflux/spring-webflux-5.3/library/src/main/java/io/opentelemetry/instrumentation/spring/webflux/v5_3/internal/SpringWebfluxBuilderUtil.java
2 parents 4ad8436 + 4250c0c commit 0429d64

729 files changed

Lines changed: 3081 additions & 2846 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/agents/knowledge/general-rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ When a "Knowledge File" is listed, load it from `knowledge/` before reviewing th
2727
| Semconv | Dual semconv testing | `SemconvStability`, `maybeStable`, semconv Gradle tasks | `testing-semconv-stability.md` |
2828
| Testing | General test patterns | Test files in scope — assertion style, test method signatures and throws clauses, resource cleanup, attribute assertions | `testing-general-patterns.md` |
2929
| Testing | Experimental flag tests | `testExperimental`, experimental attribute assertions, `experimental` flags in JVM args or system properties | `testing-experimental-flags.md` |
30+
| Testing | Flag-gated / mode-dependent assertion shape (experimental, `testLatestDeps`, semconv) — shared accessor (`testLatestDeps()`, `emitStable*Semconv()`) or `EXPERIMENTAL_ATTRIBUTES` constant, inline ternary with `null` for "absent" | Test classes branching on `EXPERIMENTAL_ATTRIBUTES`, `testLatestDeps()`, or `emitOld*`/`emitStable*` | `testing-general-patterns.md` |
3031
| Library | TelemetryBuilder/getter/setter patterns | Library instrumentation classes | `library-patterns.md` |
3132
| API | Deprecation and breaking-change policy | Public API changes | `api-deprecation-policy.md` |
3233
| Config | Config property stability/renames/removals | `otel.instrumentation.*` property changes, `DeclarativeConfigUtil` or `ConfigProperties` usage | `config-property-stability.md` |

.github/agents/knowledge/gradle-conventions.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ Flag `build.gradle.kts` dependencies that appear unused or redundant:
157157
- A dependency that duplicates something already provided transitively.
158158
- A `testImplementation` dependency for a library not used in tests.
159159

160+
### Never declare `javaagent-bootstrap` explicitly in javaagent modules
161+
162+
The `otel.javaagent-instrumentation` convention plugin already provides
163+
`javaagent-bootstrap` on the `compileOnly` classpath transitively. Do not add
164+
`compileOnly(project(":javaagent-bootstrap"))` to a javaagent module's
165+
`build.gradle.kts`, and remove it if present.
166+
160167
## Custom Test Tasks
161168

162169
Every custom `Test` task registered with `val foo by registering(Test::class)` **must** include
@@ -215,15 +222,16 @@ block, not repeated on each individual task.
215222
If a property or JVM arg is moved into `withType<Test>().configureEach`, remove any now-redundant
216223
copies from individual tasks unless a task intentionally overrides the shared value.
217224

218-
When the module's `build.gradle.kts` does not explicitly register additional `Test` tasks,
219-
`tasks.test { ... }` is fine — **do not** convert it to `withType<Test>().configureEach` and
220-
do not flag it.
225+
**When the module has only a single test task, prefer the simple `tasks.test { ... }` form.**
226+
Do **not** convert `tasks.test { ... }` to `withType<Test>().configureEach` in single-test-task
227+
modules, and do **not** flag the simple form as a problem. The `withType<Test>().configureEach`
228+
form is only justified when the same `build.gradle.kts` actually registers additional `Test` tasks.
221229

222230
**`latestDepTest` does not count as a second test task for this rule.** It is registered
223231
implicitly by the convention plugin when `testLatestDeps` is set, and it inherits the
224232
configuration of `tasks.test`. A module with only a `tasks.test { ... }` block and no
225233
`by registering(Test::class)` declarations is a single-test-task module — leave it alone
226-
even if `testLatestDeps = true`.
234+
(use the simple form) even if `testLatestDeps = true`.
227235

228236
Only consider converting to `withType<Test>().configureEach` when the **same
229237
`build.gradle.kts`** explicitly registers one or more additional `Test` tasks via
@@ -261,9 +269,11 @@ review**. Only verify correctness when they are already present.
261269

262270
When already present, verify:
263271

264-
- `collectMetadata` is in `withType<Test>().configureEach` (or `tasks.test` if the module
265-
does not explicitly register additional `Test` tasks — `latestDepTest` does not count) —
266-
never on individual tasks.
272+
- `collectMetadata` is in `tasks.test` for single-test-task modules, or in
273+
`withType<Test>().configureEach` for modules that explicitly register additional `Test`
274+
tasks via `by registering(Test::class)` (`latestDepTest` does not count) — never on
275+
individual tasks. Do not use
276+
`withType<Test>().configureEach { ... }` in single-test-task modules.
267277
- `metadataConfig` is on each non-default task. It may also appear on the default `test`
268278
task when that task itself runs with non-default `jvmArgs` (e.g., an experimental flag
269279
enabled module-wide via `withType<Test>().configureEach { jvmArgs(...) }`); in that case

.github/agents/knowledge/testing-experimental-flags.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ block but there is no dedicated `testExperimental` task, fix it:
2828

2929
1. Create a `testExperimental` task (see Gradle Task Setup below).
3030
2. Move the experimental flag out of the shared/default task config into `testExperimental`.
31-
3. Add a `private static final boolean EXPERIMENTAL_ATTRIBUTES` field to test classes.
32-
4. Wrap experimental attribute assertions with the `experimental()` helper.
33-
5. Wire the new task into `check`.
31+
3. Add a `private static final boolean EXPERIMENTAL_ATTRIBUTES` field to test classes and
32+
gate experimental attribute assertions on it (see Java Test Patterns below).
33+
4. Wire the new task into `check`.
3434

3535
## Gradle Task Setup
3636

@@ -51,30 +51,38 @@ val testExperimental by registering(Test::class) {
5151

5252
## Java Test Patterns
5353

54-
Read the flag once into a `private static final boolean` at class level:
54+
For the cross-cutting shape — inline ternary with `null` for "absent", when to use top-level
55+
`if` blocks, and `assumeTrue(...)` guidance — see
56+
[testing-general-patterns.md](testing-general-patterns.md#flag-gated--mode-dependent-assertions).
57+
The experimental-specific patterns below build on that shape.
58+
59+
### Hoist the flag into a per-class `EXPERIMENTAL_ATTRIBUTES` constant
60+
61+
The property name is module-specific, so there is no shared accessor. Read it once into a
62+
`private static final boolean` near the top of the class and reference the constant
63+
everywhere in the file:
5564

5665
```java
5766
private static final boolean EXPERIMENTAL_ATTRIBUTES =
5867
Boolean.getBoolean("otel.instrumentation.<module>.experimental-span-attributes");
5968
```
6069

61-
Use inline ternary in assertions — `null` means attribute expected absent:
70+
When multiple experimental flags coexist in one class, use a more specific name per flag.
6271

63-
```java
64-
equalTo(ExperimentalAttributes.SOME_ATTR, EXPERIMENTAL_ATTRIBUTES ? "value" : null)
65-
```
72+
### Single-arg `experimental(value)` helper
6673

67-
When many assertions share the flag, extract an `experimental()` helper:
74+
Experimental attributes are by definition absent when the flag is off, so the off-branch is
75+
always `null`. When several assertions in the same class gate attributes on
76+
`EXPERIMENTAL_ATTRIBUTES`, extract a tiny helper:
6877

6978
```java
7079
@Nullable
7180
private static <T> T experimental(T value) {
7281
return EXPERIMENTAL_ATTRIBUTES ? value : null;
7382
}
83+
84+
equalTo(SOME_KEY, experimental("value"))
7485
```
7586

7687
For multiple test classes sharing the same flag, move the helper into a shared
7788
`ExperimentalTestHelper` class and static-import it.
78-
79-
Use `assumeTrue(EXPERIMENTAL_ATTRIBUTES)` only when an entire test is meaningful in
80-
experimental mode only — prefer the ternary/helper pattern so both modes are exercised.

.github/agents/knowledge/testing-general-patterns.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,41 @@ unless the span ordering within a trace is genuinely non-deterministic (e.g., co
135135
producers/consumers, thread-pool fan-out, or channel interleaving). Sequential operations
136136
like `repeat {}` loops, single-child traces, and `flux` sequential emission produce spans
137137
in deterministic order — use `hasSpansSatisfyingExactly` for those.
138+
139+
## Flag-Gated / Mode-Dependent Assertions
140+
141+
Several test modes change which attributes, span names, status codes, or span indexes are
142+
expected:
143+
144+
- Experimental attributes (`-Dotel.instrumentation.<module>.experimental-*=true`) — see
145+
[testing-experimental-flags.md](testing-experimental-flags.md).
146+
- Semconv stability (`-Dotel.semconv-stability.opt-in=...`) — see
147+
[testing-semconv-stability.md](testing-semconv-stability.md).
148+
- `testLatestDeps` Gradle property — runs against the newest supported library versions
149+
instead of the pinned earliest-supported ones.
150+
151+
### Read the flag through a shared static helper, not a per-class field
152+
153+
Each flag has a shared static accessor; static-import it and call it directly. Never call
154+
`Boolean.getBoolean("…")` inline and never duplicate the property-name string at the call
155+
site.
156+
157+
| Flag | Shared accessor | Where it lives |
158+
| --- | --- | --- |
159+
| `-PtestLatestDeps=true` | `testLatestDeps()` | `io.opentelemetry.instrumentation.testing.util.TestLatestDeps` (testing-common) |
160+
| `otel.semconv-stability.opt-in=…` | `emitStableDatabaseSemconv()`, `emitOldDatabaseSemconv()`, `emitStableCodeSemconv()`, etc. | `io.opentelemetry.instrumentation.api.internal.SemconvStability` |
161+
| `otel.instrumentation.<module>.experimental-*` | per-module `EXPERIMENTAL_ATTRIBUTES` constant — see [testing-experimental-flags.md](testing-experimental-flags.md) | within the test class |
162+
163+
### Inline ternary in `equalTo(...)` with `null` for "absent"
164+
165+
Push the ternary as deep as possible — into the `equalTo` value or single attribute key —
166+
rather than duplicating two whole `hasAttributesSatisfyingExactly(...)` blocks under a
167+
`flag ? a : b`. The assertion API treats `null` as "expect attribute absent":
168+
169+
```java
170+
equalTo(DB_USER, emitStableDatabaseSemconv() ? null : USER_DB)
171+
equalTo(ERROR_TYPE, emitStableDatabaseSemconv() ? "42601" : null)
172+
equalTo(SOME_KEY, EXPERIMENTAL_ATTRIBUTES ? "value" : null)
173+
span.hasName(testLatestDeps() ? "GET" : "HTTP GET")
174+
.hasParent(trace.getSpan(testLatestDeps() ? 0 : 1))
175+
```

.github/agents/knowledge/testing-semconv-stability.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,30 @@ for other domains `check { dependsOn(testStableSemconv) }`.
7676

7777
## Asserting Attributes in Tests
7878

79-
Preferred approach (most compact) — inline ternary with `emitStable*()`, where `null` means
80-
the attribute is expected absent:
79+
For the cross-cutting shape — inline ternary with `null` for "absent", static-imported flag
80+
accessors, and `assumeTrue(...)` guidance — see
81+
[testing-general-patterns.md](testing-general-patterns.md#flag-gated--mode-dependent-assertions).
82+
The semconv-specific patterns below build on that shape.
8183

82-
```java
83-
equalTo(DB_USER, emitStableDatabaseSemconv() ? null : USER_DB)
84-
equalTo(ERROR_TYPE, emitStableDatabaseSemconv() ? "42601" : null)
85-
span.hasName(emitStableDatabaseSemconv() ? "SELECT" : "SELECT dbname")
86-
```
84+
### `maybeStable(OLD_KEY)` for 1:1 key renames
8785

88-
Use `maybeStable(oldKey)` when only the attribute key changes and the value stays the same.
89-
`maybeStable()` does NOT cover `dup` mode — use separate `if` blocks for that.
86+
Use `maybeStable(OLD_KEY)` when only the attribute *key* flips between old and stable
87+
semconv and the value is identical:
9088

9189
```java
9290
span.hasAttribute(equalTo(maybeStable(DB_STATEMENT), "SELECT ?"));
9391
```
9492

95-
Use separate `if` blocks (not `if/else`) when assertion structure differs significantly between
96-
modes — this ensures both branches execute in `dup` mode:
93+
`maybeStable()` does **not** cover `/dup` mode (it returns one key, not both), and does
94+
**not** apply where the mapping isn't 1:1 — for example `DB_RESPONSE_STATUS_CODE`
95+
`ERROR_TYPE`. Use `emitOld*()` / `emitStable*()` `if` blocks for those.
96+
97+
### `if` blocks (not `if/else`) when structure differs
98+
99+
When the *set* of asserted attributes differs between modes — not just values — use
100+
separate top-level `if` blocks rather than `if/else`. For domains that support `/dup` mode
101+
(currently RPC), this is required so both branches run; for other domains it's a habit
102+
that keeps the assertion `/dup`-safe if the domain ever adopts it:
97103

98104
```java
99105
if (emitStableCodeSemconv()) {
@@ -104,11 +110,6 @@ if (emitOldCodeSemconv()) {
104110
}
105111
```
106112

107-
Use `assumeTrue(emitStable*())` only when an entire test is meaningful in one mode only.
108-
109113
## Key Rules
110114

111115
- Add `@SuppressWarnings("deprecation")` at class level when tests use old Semconv constants.
112-
- Use `if` (not `if/else`) for dual-mode assertions so both branches run in `/dup` mode.
113-
- Do NOT use `maybeStable()` for `DB_RESPONSE_STATUS_CODE``ERROR_TYPE` — these don't have
114-
a 1:1 mapping. Use `emitOld*()`/`emitStable*()` `if` blocks instead.

.github/config/latest-dep-versions.json

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"com.ning:async-http-client#1.8.+": "1.8.17",
6161
"com.ning:async-http-client#1.9.+": "1.9.40",
6262
"com.noelios.restlet:com.noelios.restlet#+": "1.1.10",
63-
"com.openai:openai-java#+": "4.32.0",
63+
"com.openai:openai-java#+": "4.33.0",
6464
"com.oracle.database.jdbc:ojdbc8#+": "23.26.1.0.0",
6565
"com.oracle.database.jdbc:ucp#+": "23.26.1.0.0",
6666
"com.rabbitmq:amqp-client#+": "5.30.0",
@@ -110,7 +110,7 @@
110110
"com.typesafe.play:play_2.11#+": "2.7.9",
111111
"com.typesafe.play:play_2.12#+": "2.8.22",
112112
"com.typesafe.play:play_2.13#+": "2.9.10",
113-
"com.vaadin:flow-server#+": "25.1.3",
113+
"com.vaadin:flow-server#+": "25.1.4",
114114
"com.vaadin:vaadin-spring-boot-starter#14.11.+": "14.11.14",
115115
"com.xuxueli:xxl-job-core#+": "3.4.0",
116116
"com.xuxueli:xxl-job-core#2.2.+": "2.2.0",
@@ -308,16 +308,16 @@
308308
"org.apache.pekko:pekko-http_3#+": "1.3.0",
309309
"org.apache.pekko:pekko-stream_2.12#+": "1.4.0",
310310
"org.apache.pekko:pekko-stream_2.13#+": "1.5.0",
311-
"org.apache.pulsar:pulsar-client#+": "4.2.0",
312-
"org.apache.pulsar:pulsar-client-admin#+": "4.2.0",
311+
"org.apache.pulsar:pulsar-client#+": "4.2.1",
312+
"org.apache.pulsar:pulsar-client-admin#+": "4.2.1",
313313
"org.apache.rocketmq:rocketmq-client#+": "5.5.0",
314314
"org.apache.rocketmq:rocketmq-client-java#+": "5.2.0",
315315
"org.apache.rocketmq:rocketmq-test#+": "5.5.0",
316316
"org.apache.shardingsphere.elasticjob:elasticjob-lite-core#+": "3.0.4",
317317
"org.apache.shenyu:shenyu-spring-boot-starter-gateway#+": "2.7.0.3",
318318
"org.apache.shenyu:shenyu-web#+": "2.7.0.3",
319319
"org.apache.struts:struts2-core#+": "7.1.1",
320-
"org.apache.struts:struts2-core#6.+": "6.8.0",
320+
"org.apache.struts:struts2-core#6.+": "6.9.0",
321321
"org.apache.tapestry:tapestry-core#+": "5.9.1",
322322
"org.apache.tomcat.embed:tomcat-embed-core#+": "11.0.21",
323323
"org.apache.tomcat.embed:tomcat-embed-core#9.+": "9.0.117",
@@ -523,24 +523,24 @@
523523
"org.springframework:spring-webmvc#+": "7.0.7",
524524
"org.testcontainers:testcontainers-kafka#+": "2.0.5",
525525
"org.vibur:vibur-dbcp#+": "26.0",
526-
"redis.clients:jedis#+": "7.4.1",
526+
"redis.clients:jedis#+": "7.5.0",
527527
"redis.clients:jedis#2.+": "2.10.2",
528528
"redis.clients:jedis#3.+": "3.10.0",
529529
"software.amazon.awssdk.crt:aws-crt#+": "0.45.1",
530-
"software.amazon.awssdk:aws-core#+": "2.42.41",
531-
"software.amazon.awssdk:aws-json-protocol#+": "2.42.41",
532-
"software.amazon.awssdk:bedrockruntime#+": "2.42.41",
533-
"software.amazon.awssdk:dynamodb#+": "2.42.41",
534-
"software.amazon.awssdk:ec2#+": "2.42.41",
535-
"software.amazon.awssdk:kinesis#+": "2.42.41",
536-
"software.amazon.awssdk:lambda#+": "2.42.41",
537-
"software.amazon.awssdk:rds#+": "2.42.41",
538-
"software.amazon.awssdk:s3#+": "2.42.41",
539-
"software.amazon.awssdk:secretsmanager#+": "2.42.41",
540-
"software.amazon.awssdk:ses#+": "2.42.41",
541-
"software.amazon.awssdk:sfn#+": "2.42.41",
542-
"software.amazon.awssdk:sns#+": "2.42.41",
543-
"software.amazon.awssdk:sqs#+": "2.42.41",
530+
"software.amazon.awssdk:aws-core#+": "2.43.0",
531+
"software.amazon.awssdk:aws-json-protocol#+": "2.43.0",
532+
"software.amazon.awssdk:bedrockruntime#+": "2.43.0",
533+
"software.amazon.awssdk:dynamodb#+": "2.43.0",
534+
"software.amazon.awssdk:ec2#+": "2.43.0",
535+
"software.amazon.awssdk:kinesis#+": "2.43.0",
536+
"software.amazon.awssdk:lambda#+": "2.43.0",
537+
"software.amazon.awssdk:rds#+": "2.43.0",
538+
"software.amazon.awssdk:s3#+": "2.43.0",
539+
"software.amazon.awssdk:secretsmanager#+": "2.43.0",
540+
"software.amazon.awssdk:ses#+": "2.43.0",
541+
"software.amazon.awssdk:sfn#+": "2.43.0",
542+
"software.amazon.awssdk:sns#+": "2.43.0",
543+
"software.amazon.awssdk:sqs#+": "2.43.0",
544544
"tech.powerjob:powerjob-official-processors#+": "5.1.2",
545545
"tech.powerjob:powerjob-worker#+": "5.1.2"
546546
}

.github/repository-settings.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ Secrets:
2424

2525
## Secrets and variables > Actions
2626

27-
### Repository secrets
28-
29-
- `FLAKY_TEST_REPORTER_ACCESS_KEY` - owned by [@laurit](https://github.com/laurit)
30-
3127
### Organization secrets
3228

3329
- `DEVELOCITY_ACCESS_KEY` (scoped only to Java repos)

.github/scripts/draft-release-notes/classify.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ def iter_bundles() -> list[PrBundle]:
128128

129129
def preclassify(bundle: PrBundle) -> dict | None:
130130
"""Return a decision dict if we can decide without the LLM, else None."""
131+
labels = bundle.meta.get("labels") or []
132+
if "automated code review" in labels:
133+
return {
134+
"decision": "omit",
135+
"section": None,
136+
"surface": "automated code review sweep",
137+
"user_visible_effect": "none",
138+
"bullet": None,
139+
"evidence": "PR labeled 'automated code review'",
140+
"source": "preclassify",
141+
}
131142
if not bundle.meta.get("touches_src_main"):
132143
files = [f["path"] for f in bundle.meta.get("files", [])]
133144
return {
@@ -244,13 +255,34 @@ def parse_response(s: str) -> dict:
244255
s = s.strip()
245256
s = re.sub(r"^```(?:json)?\s*", "", s, flags=re.I)
246257
s = re.sub(r"\s*```$", "", s)
247-
# Some CLIs prefix a status line; try to locate the first { and last }.
248-
if not s.startswith("{"):
249-
start = s.find("{")
250-
end = s.rfind("}")
251-
if start != -1 and end != -1:
252-
s = s[start : end + 1]
253-
return json.loads(s)
258+
# The model sometimes emits scratchpad objects (e.g. {"intent": "..."})
259+
# before the real decision object. Walk all top-level JSON objects in
260+
# the string and return the last one that has a "decision" key, falling
261+
# back to the last object if none match.
262+
decoder = json.JSONDecoder()
263+
objects: list[dict] = []
264+
i = 0
265+
n = len(s)
266+
while i < n:
267+
# Skip to the next object start.
268+
j = s.find("{", i)
269+
if j == -1:
270+
break
271+
try:
272+
obj, end = decoder.raw_decode(s, j)
273+
except json.JSONDecodeError:
274+
i = j + 1
275+
continue
276+
if isinstance(obj, dict):
277+
objects.append(obj)
278+
i = end
279+
if not objects:
280+
# Force the original error path for callers that expect JSONDecodeError.
281+
return json.loads(s)
282+
for obj in reversed(objects):
283+
if "decision" in obj:
284+
return obj
285+
return objects[-1]
254286

255287

256288
def validate(decision: dict) -> list[str]:
@@ -356,7 +388,7 @@ def process_one(bundle: PrBundle, args) -> tuple[str, str | None, dict | None]:
356388
def main() -> int:
357389
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
358390
ap.add_argument("--jobs", type=int, default=4, help="parallel CLI invocations (default 4)")
359-
ap.add_argument("--timeout", type=int, default=300, help="per-PR CLI timeout seconds")
391+
ap.add_argument("--timeout", type=int, default=900, help="per-PR CLI timeout seconds")
360392
ap.add_argument("--force", action="store_true", help="re-classify PRs with existing decision.json")
361393
ap.add_argument("--only", type=int, nargs="*", help="restrict to these PR numbers")
362394
ap.add_argument(

0 commit comments

Comments
 (0)