Skip to content

Commit 7c51d40

Browse files
authored
Improve "Update smoke test latest versions" workflow (#11303)
Use current version until better upgrade Prioritize GA over pre-release Merge branch 'master' into sarahchen6/clean-smoke-test-workflow-comment Co-authored-by: sarah.chen <sarah.chen@datadoghq.com>
1 parent 9984682 commit 7c51d40

4 files changed

Lines changed: 199 additions & 21 deletions

File tree

.github/scripts/dependency_age.py

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def parse_args() -> argparse.Namespace:
5757
def add_common_selection_args(parser: argparse.ArgumentParser) -> None:
5858
parser.add_argument("--min-age-hours", type=int, default=default_min_age_hours())
5959
parser.add_argument("--now")
60+
parser.add_argument("--current-version", default=None)
6061
parser.add_argument("--github-output", default=None)
6162

6263

@@ -107,6 +108,11 @@ def format_datetime(value: datetime) -> str:
107108
return value.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
108109

109110

111+
# normalize datetime to YYYY-MM-DD date for more readable PR comment outputs
112+
def format_date(value: datetime) -> str:
113+
return value.astimezone(timezone.utc).strftime("%Y-%m-%d")
114+
115+
110116
# emit key=value lines to stdout and GitHub Actions output file
111117
def emit_outputs(outputs: dict[str, Any], github_output: str | None) -> None:
112118
lines = [f"{key}={'' if value is None else value}" for key, value in outputs.items()]
@@ -156,6 +162,7 @@ def select_gradle_release(args: argparse.Namespace) -> int:
156162
not_found_reason=(
157163
f"No eligible stable Gradle release is at least {args.min_age_hours} hours old."
158164
),
165+
current_version=args.current_version,
159166
)
160167

161168

@@ -189,6 +196,7 @@ def select_maven_release(args: argparse.Namespace) -> int:
189196
f"No eligible stable release found for {args.group_id}:{args.artifact_id} "
190197
f"that is at least {args.min_age_hours} hours old."
191198
),
199+
current_version=args.current_version,
192200
)
193201

194202

@@ -240,15 +248,24 @@ def load_maven_documents(
240248

241249
# parse a version string into a tuple of ints for numeric comparison (e.g. "3.9.11" → (3, 9, 11))
242250
def _version_sort_key(version: str) -> tuple:
243-
parts = []
251+
segments = []
244252
for segment in re.split(r"([.\-])", version):
245253
if segment in {"", ".", "-"}:
246254
continue
247255
try:
248-
parts.append((0, int(segment)))
256+
segments.append((0, int(segment)))
249257
except ValueError:
250-
parts.append((1, segment))
251-
return tuple(parts)
258+
segments.append((1, segment))
259+
260+
release = []
261+
prerelease = []
262+
for i, seg in enumerate(segments):
263+
if seg[0] == 1: # first string segment starts the prerelease part
264+
prerelease = segments[i:]
265+
break
266+
release.append(seg)
267+
268+
return (tuple(release), not bool(prerelease), tuple(prerelease))
252269

253270

254271
# emit selection result to stdout and GitHub Actions output file for select-gradle and select-maven
@@ -259,11 +276,37 @@ def emit_selection_result(
259276
github_output: str | None,
260277
candidates: list[Candidate],
261278
not_found_reason: str,
279+
current_version: str | None = None,
262280
) -> int:
263281
selected = max(candidates, key=lambda candidate: _version_sort_key(candidate.version), default=None)
264-
outputs: dict[str, Any] = {
265-
"cutoff_at": format_datetime(cutoff),
266-
}
282+
outputs: dict[str, Any] = {}
283+
284+
# If the current version is already >= the best candidate, keep it
285+
if current_version and (
286+
not selected
287+
or _version_sort_key(current_version) >= _version_sort_key(selected.version)
288+
):
289+
outputs.update(
290+
{
291+
"found": "true",
292+
"version": current_version,
293+
"published_at": "",
294+
"reason": "",
295+
}
296+
)
297+
emit_outputs(outputs, github_output)
298+
if selected:
299+
print(
300+
f"Current version {current_version} for {label} is already >= "
301+
f"latest eligible {selected.version}; keeping current version."
302+
)
303+
else:
304+
print(
305+
f"No eligible version found for {label}; "
306+
f"keeping current version {current_version}."
307+
)
308+
return 0
309+
267310
if not selected:
268311
outputs.update(
269312
{
@@ -281,14 +324,14 @@ def emit_selection_result(
281324
{
282325
"found": "true",
283326
"version": selected.version,
284-
"published_at": format_datetime(selected.published_at),
327+
"published_at": format_date(selected.published_at),
285328
"reason": "",
286329
}
287330
)
288331
emit_outputs(outputs, github_output)
289332
print(
290333
f"Selected latest eligible stable version for {label}: "
291-
f"{selected.version} (published {format_datetime(selected.published_at)}, cutoff {format_datetime(cutoff)})"
334+
f"{selected.version} (published {format_date(selected.published_at)})"
292335
)
293336
return 0
294337

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"response": {
3+
"docs": [
4+
{
5+
"v": "4.0.0",
6+
"timestamp": "2026-04-20T12:00:00Z"
7+
},
8+
{
9+
"v": "4.0.0-beta-3",
10+
"timestamp": "2026-04-18T12:00:00Z"
11+
},
12+
{
13+
"v": "3.9.8",
14+
"timestamp": "2026-04-15T12:00:00Z"
15+
}
16+
]
17+
}
18+
}

.github/scripts/tests/test_dependency_age.py

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
FIXTURES = Path(__file__).resolve().parent / "fixtures"
1111
NOW = "2026-04-24T12:00:00Z"
1212
OUTPUT_PATTERN = re.compile(
13-
r"^(cutoff_at|found|version|published_at|reason)=(.*)$"
13+
r"^(found|version|published_at|reason)=(.*)$"
1414
)
1515

1616

@@ -48,8 +48,7 @@ def test_selects_previous_gradle_release_when_newest_is_too_new(self) -> None:
4848
self.assertEqual(result.returncode, 0, result.stderr)
4949
outputs = self.parse_outputs(result.stdout)
5050
self.assertEqual(outputs["version"], "9.4.1")
51-
self.assertEqual(outputs["published_at"], "2026-04-22T11:00:00Z")
52-
self.assertEqual(outputs["cutoff_at"], "2026-04-22T12:00:00Z")
51+
self.assertEqual(outputs["published_at"], "2026-04-22")
5352

5453
def test_reports_when_no_eligible_gradle_release_exists(self) -> None:
5554
result = self.run_script(
@@ -121,7 +120,104 @@ def test_exact_48_hour_boundary_is_accepted(self) -> None:
121120
self.assertEqual(result.returncode, 0, result.stderr)
122121
outputs = self.parse_outputs(result.stdout)
123122
self.assertEqual(outputs["version"], "3.5.5")
124-
self.assertEqual(outputs["published_at"], "2026-04-22T12:00:00Z")
123+
self.assertEqual(outputs["published_at"], "2026-04-22")
124+
125+
126+
def test_ga_version_overrides_current_prerelease(self) -> None:
127+
result = self.run_script(
128+
"select-maven",
129+
"--now",
130+
NOW,
131+
"--group-id",
132+
"org.apache.maven",
133+
"--artifact-id",
134+
"apache-maven",
135+
"--search-response-file",
136+
str(FIXTURES / "maven-ga-replaces-beta.json"),
137+
"--prerelease-pattern",
138+
"alpha",
139+
"--prerelease-pattern",
140+
"beta",
141+
"--prerelease-pattern",
142+
"rc",
143+
"--current-version",
144+
"4.0.0-beta-3",
145+
)
146+
147+
self.assertEqual(result.returncode, 0, result.stderr)
148+
outputs = self.parse_outputs(result.stdout)
149+
self.assertEqual(outputs["found"], "true")
150+
self.assertEqual(outputs["version"], "4.0.0")
151+
self.assertEqual(outputs["published_at"], "2026-04-20")
152+
153+
def test_keeps_current_version_when_higher_than_eligible(self) -> None:
154+
result = self.run_script(
155+
"select-maven",
156+
"--now",
157+
NOW,
158+
"--group-id",
159+
"org.apache.maven",
160+
"--artifact-id",
161+
"apache-maven",
162+
"--search-response-file",
163+
str(FIXTURES / "maven-newest-too-new.json"),
164+
"--prerelease-pattern",
165+
"alpha",
166+
"--prerelease-pattern",
167+
"beta",
168+
"--prerelease-pattern",
169+
"rc",
170+
"--current-version",
171+
"4.0.0-beta-3",
172+
)
173+
174+
self.assertEqual(result.returncode, 0, result.stderr)
175+
outputs = self.parse_outputs(result.stdout)
176+
self.assertEqual(outputs["found"], "true")
177+
self.assertEqual(outputs["version"], "4.0.0-beta-3")
178+
self.assertEqual(outputs["published_at"], "")
179+
180+
def test_updates_when_eligible_version_is_higher_than_current(self) -> None:
181+
result = self.run_script(
182+
"select-maven",
183+
"--now",
184+
NOW,
185+
"--group-id",
186+
"org.apache.maven.plugins",
187+
"--artifact-id",
188+
"maven-surefire-plugin",
189+
"--search-response-file",
190+
str(FIXTURES / "surefire-boundary.json"),
191+
"--prerelease-pattern",
192+
"alpha",
193+
"--prerelease-pattern",
194+
"beta",
195+
"--current-version",
196+
"3.5.4",
197+
)
198+
199+
self.assertEqual(result.returncode, 0, result.stderr)
200+
outputs = self.parse_outputs(result.stdout)
201+
self.assertEqual(outputs["found"], "true")
202+
self.assertEqual(outputs["version"], "3.5.5")
203+
self.assertEqual(outputs["published_at"], "2026-04-22")
204+
205+
def test_keeps_current_version_when_no_eligible_version_exists(self) -> None:
206+
result = self.run_script(
207+
"select-gradle",
208+
"--now",
209+
NOW,
210+
"--versions-file",
211+
str(FIXTURES / "gradle-no-eligible.json"),
212+
"--current-version",
213+
"9.0.0",
214+
)
215+
216+
self.assertEqual(result.returncode, 0, result.stderr)
217+
outputs = self.parse_outputs(result.stdout)
218+
self.assertEqual(outputs["found"], "true")
219+
self.assertEqual(outputs["version"], "9.0.0")
220+
self.assertEqual(outputs["published_at"], "")
125221

126222

127223
if __name__ == "__main__":

.github/workflows/update-smoke-test-latest-versions.yaml

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,22 @@ jobs:
2929
DATE=$(date +'%Y%m%d')
3030
echo "branch=ci/update-smoke-test-latest-versions-${DATE}" >> "$GITHUB_OUTPUT"
3131
32+
- name: Read current pinned versions
33+
id: current
34+
run: |
35+
gradle_props="dd-smoke-tests/gradle/src/test/resources/latest-tool-versions.properties"
36+
maven_props="dd-smoke-tests/maven/src/test/resources/latest-tool-versions.properties"
37+
get_prop() { grep "^$1=" "$2" 2>/dev/null | cut -d= -f2 || true; }
38+
echo "gradle_version=$(get_prop gradle.version "$gradle_props")" >> "$GITHUB_OUTPUT"
39+
echo "maven_version=$(get_prop maven.version "$maven_props")" >> "$GITHUB_OUTPUT"
40+
echo "surefire_version=$(get_prop maven-surefire.version "$maven_props")" >> "$GITHUB_OUTPUT"
41+
3242
- name: Resolve latest eligible Gradle version
3343
id: gradle
3444
run: |
3545
python3 .github/scripts/dependency_age.py select-gradle \
3646
--min-age-hours "${MIN_DEPENDENCY_AGE_HOURS}" \
47+
--current-version "${{ steps.current.outputs.gradle_version }}" \
3748
--github-output "$GITHUB_OUTPUT"
3849
3950
- name: Resolve latest eligible stable Maven version
@@ -46,6 +57,7 @@ jobs:
4657
--prerelease-pattern beta \
4758
--prerelease-pattern rc \
4859
--min-age-hours "${MIN_DEPENDENCY_AGE_HOURS}" \
60+
--current-version "${{ steps.current.outputs.maven_version }}" \
4961
--github-output "$GITHUB_OUTPUT"
5062
5163
- name: Resolve latest eligible stable Maven Surefire version
@@ -57,19 +69,29 @@ jobs:
5769
--prerelease-pattern alpha \
5870
--prerelease-pattern beta \
5971
--min-age-hours "${MIN_DEPENDENCY_AGE_HOURS}" \
72+
--current-version "${{ steps.current.outputs.surefire_version }}" \
6073
--github-output "$GITHUB_OUTPUT"
6174
6275
- name: Update properties files
76+
id: update
6377
env:
6478
GRADLE_VERSION: ${{ steps.gradle.outputs.version }}
79+
GRADLE_PUBLISHED: ${{ steps.gradle.outputs.published_at }}
6580
MAVEN_VERSION: ${{ steps.maven.outputs.version }}
81+
MAVEN_PUBLISHED: ${{ steps.maven.outputs.published_at }}
6682
SUREFIRE_VERSION: ${{ steps.surefire.outputs.version }}
83+
SUREFIRE_PUBLISHED: ${{ steps.surefire.outputs.published_at }}
6784
run: |
85+
version_line() { if [ -n "$2" ]; then echo "$1 (published $2)"; else echo "$1 (unchanged)"; fi; }
6886
echo "Writing latest eligible stable versions (>=${MIN_DEPENDENCY_AGE_HOURS}h old) to properties files:"
69-
echo " Gradle: ${GRADLE_VERSION} (published ${{ steps.gradle.outputs.published_at }})"
70-
echo " Maven: ${MAVEN_VERSION} (published ${{ steps.maven.outputs.published_at }})"
71-
echo " Maven Surefire: ${SUREFIRE_VERSION} (published ${{ steps.surefire.outputs.published_at }})"
72-
echo " Eligibility cutoff: ${{ steps.gradle.outputs.cutoff_at }}"
87+
echo " Gradle: $(version_line "${GRADLE_VERSION}" "${GRADLE_PUBLISHED}")"
88+
echo " Maven: $(version_line "${MAVEN_VERSION}" "${MAVEN_PUBLISHED}")"
89+
echo " Maven Surefire: $(version_line "${SUREFIRE_VERSION}" "${SUREFIRE_PUBLISHED}")"
90+
91+
# Build version lines for PR body
92+
echo "gradle_line=$(version_line "${GRADLE_VERSION}" "${GRADLE_PUBLISHED}")" >> "$GITHUB_OUTPUT"
93+
echo "maven_line=$(version_line "${MAVEN_VERSION}" "${MAVEN_PUBLISHED}")" >> "$GITHUB_OUTPUT"
94+
echo "surefire_line=$(version_line "${SUREFIRE_VERSION}" "${SUREFIRE_PUBLISHED}")" >> "$GITHUB_OUTPUT"
7395
7496
printf '%s\n' \
7597
"# Pinned latest eligible stable versions (>=${MIN_DEPENDENCY_AGE_HOURS}h old) for CI Visibility Gradle smoke tests." \
@@ -138,10 +160,9 @@ jobs:
138160
This PR updates the pinned latest eligible stable tool versions used by CI Visibility smoke tests.
139161
Only releases at least ${{ env.MIN_DEPENDENCY_AGE_HOURS }} hours old are eligible.
140162
141-
- Gradle: ${{ steps.gradle.outputs.version }} (published ${{ steps.gradle.outputs.published_at }})
142-
- Maven: ${{ steps.maven.outputs.version }} (published ${{ steps.maven.outputs.published_at }})
143-
- Maven Surefire: ${{ steps.surefire.outputs.version }} (published ${{ steps.surefire.outputs.published_at }})
144-
- Eligibility cutoff: ${{ steps.gradle.outputs.cutoff_at }}
163+
- Gradle: ${{ steps.update.outputs.gradle_line }}
164+
- Maven: ${{ steps.update.outputs.maven_line }}
165+
- Maven Surefire: ${{ steps.update.outputs.surefire_line }}
145166
146167
# Motivation
147168

0 commit comments

Comments
 (0)