Skip to content

Commit d5af07d

Browse files
X-GuardianSimon Heather
andauthored
fix(cli): resolve prebuilt Java provider versions via repo1.maven.org (#285)
### Description The Java integration tests (`provider-add`, `provider-list`, `provider-upgrade`) have been intermittently failing, resolving a prebuilt provider package version one or two below the expected maximum (e.g. `0.2.55` → `0.2.53`/`0.2.54`). #### Example Failed jobs - https://github.com/open-constructs/cdk-terrain/actions/runs/28284215519/job/83805855926?pr=284 - https://github.com/open-constructs/cdk-terrain/actions/runs/28284215519/job/83805856150?pr=284 #### Cause When adding a prebuilt Java provider, the resolver walks the npm candidate versions highest-to-lowest and installs the first that exists on Maven Central, using `JavaPackageManager.isNpmVersionAvailable` to decide existence. That check queries the `search.maven.org/solrsearch` index — a separate service from the artifact repository, and a flaky one: under load it returns `HTTP 502` and can take ~90s to respond. A failed search response reads as "version not found", so the resolver drops to an older package. #### Fix This PR points the existence check at the artifact repository directly: - **Probe `repo1.maven.org`** ([`package-manager.ts`](packages/@cdktn/cli-core/src/lib/dependencies/package-manager.ts)): `JavaPackageManager.isNpmVersionAvailable` requests the artifact's `.pom` directly (`https://repo1.maven.org/maven2/<group>/<artifact>/<version>/<artifact>-<version>.pom`) with a plain `fetch`, matching the shape of the other package managers' probes — `200` means present, anything else means not available. `repo1.maven.org` is the Fastly-fronted artifact CDN every Maven build already relies on, so it is both the source of truth for "is this artifact published" and reliably fast (~0.05s). **Why a direct file check rather than the `central.sonatype.com` search API:** the artifact CDN is the most reliable signal and the fastest, with the fewest moving parts. The Sonatype Central search API is the documented successor to `search.maven.org`, but it is still a search index in front of the same artifacts; querying the artifact directly depends only on the artifact itself. ### Checklist - [x] I have updated the PR title to match [CDKTN's style guide](https://github.com/open-constructs/cdk-terrain/blob/main/CONTRIBUTING.md#pull-requests-1) - [x] I have run the linter on my code locally - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the [documentation](https://github.com/open-constructs/cdk-terrain-docs/tree/main/content) if applicable - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works if applicable - [x] New and existing unit tests pass locally with my changes --------- Co-authored-by: Simon Heather <simon.heather@yulife.com>
1 parent de617db commit d5af07d

2 files changed

Lines changed: 96 additions & 9 deletions

File tree

packages/@cdktn/cli-core/src/lib/dependencies/package-manager.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,21 @@ class NugetPackageManager extends PackageManager {
521521
}
522522
}
523523

524+
/**
525+
* Shared base for the Java package managers (Maven and Gradle). Both install from Maven Central, so the check for
526+
* whether a given provider version exists is common to both and lives here; the concrete subclasses differ only in how
527+
* they declare and list dependencies (pom.xml vs build.gradle).
528+
*/
524529
abstract class JavaPackageManager extends PackageManager {
530+
/**
531+
* Reports whether `packageName@packageVersion` is published on Maven Central by probing the artifact repository for
532+
* the version's `.pom` directly: HTTP 200 means present, anything else means not available.
533+
*
534+
* @param packageName - The Java coordinates in "group.artifact" form, e.g. "com.hashicorp.cdktf-provider-random".
535+
* @param packageVersion - The exact version to check for.
536+
* @returns `true` if the `.pom` exists on Maven Central, `false` otherwise.
537+
* @throws If `packageName` is not in the expected "group.artifact" format.
538+
*/
525539
public async isNpmVersionAvailable(
526540
packageName: string,
527541
packageVersion: string,
@@ -535,23 +549,21 @@ abstract class JavaPackageManager extends PackageManager {
535549
);
536550
}
537551

538-
const packageIdentifier = parts.pop();
552+
const artifactId = parts.pop();
539553
const groupId = parts.join(".");
540554

541-
const url = `https://search.maven.org/solrsearch/select?q=g:${groupId}+AND+a:${packageIdentifier}+AND+v:${packageVersion}&rows=5&wt=json`;
555+
// Probe the artifact CDN directly for the .pom: 200 = present, anything else = not available.
556+
const groupPath = groupId.replace(/\./g, "/");
557+
const url = `https://repo1.maven.org/maven2/${groupPath}/${artifactId}/${packageVersion}/${artifactId}-${packageVersion}.pom`;
542558
logger.debug(
543-
`Trying to find package version by querying Maven Central under '${url}'`,
559+
`Checking whether ${packageName}@${packageVersion} exists on Maven Central under '${url}'`,
544560
);
545561
const response = await fetch(url);
546-
547-
const json = (await response.json()) as any;
548562
logger.debug(
549-
`Got response from the Maven package search for ${packageName}: ${JSON.stringify(
550-
json,
551-
)}`,
563+
`Maven Central responded HTTP ${response.status} for ${packageName}@${packageVersion}`,
552564
);
553565

554-
return (json?.response?.numFound ?? 0) > 0;
566+
return response.ok;
555567
}
556568
}
557569

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) HashiCorp, Inc
2+
// SPDX-License-Identifier: MPL-2.0
3+
import { mkdtempSync } from "fs";
4+
import { tmpdir } from "os";
5+
import { join } from "path";
6+
import nock from "nock";
7+
import { Language } from "@cdktn/commons";
8+
import { PackageManager } from "../../../lib/dependencies/package-manager";
9+
10+
const MAVEN_HOST = "https://repo1.maven.org";
11+
12+
describe("package-manager", () => {
13+
beforeAll(() => {
14+
nock.disableNetConnect();
15+
});
16+
17+
afterAll(() => {
18+
nock.cleanAll();
19+
nock.enableNetConnect();
20+
});
21+
22+
afterEach(() => {
23+
expect(nock.pendingMocks()).toEqual([]);
24+
nock.cleanAll();
25+
});
26+
27+
describe("JavaPackageManager.isNpmVersionAvailable", () => {
28+
let manager: PackageManager;
29+
30+
function pomPath(version: string) {
31+
// Mirrors the CDN layout probed by JavaPackageManager.isNpmVersionAvailable:
32+
// groupId path / artifactId / version / artifactId-version.pom
33+
return `/maven2/com/hashicorp/cdktf-provider-random/${version}/cdktf-provider-random-${version}.pom`;
34+
}
35+
36+
beforeEach(() => {
37+
// A bare temp dir (no build.gradle) resolves to the Maven manager.
38+
const dir = mkdtempSync(join(tmpdir(), "cdktn-pm-test-"));
39+
manager = PackageManager.forLanguage(Language.JAVA, dir);
40+
});
41+
42+
it("returns true when the .pom exists on Maven Central (HTTP 200)", async () => {
43+
nock(MAVEN_HOST).get(pomPath("0.2.64")).reply(200);
44+
45+
await expect(
46+
manager.isNpmVersionAvailable(
47+
"com.hashicorp.cdktf-provider-random",
48+
"0.2.64",
49+
),
50+
).resolves.toBe(true);
51+
});
52+
53+
it("returns false when the .pom is absent on Maven Central (HTTP 404)", async () => {
54+
nock(MAVEN_HOST).get(pomPath("0.2.64")).reply(404);
55+
56+
await expect(
57+
manager.isNpmVersionAvailable(
58+
"com.hashicorp.cdktf-provider-random",
59+
"0.2.64",
60+
),
61+
).resolves.toBe(false);
62+
});
63+
64+
it("returns false on a non-200 response from Maven Central", async () => {
65+
nock(MAVEN_HOST).get(pomPath("0.2.64")).reply(503);
66+
67+
await expect(
68+
manager.isNpmVersionAvailable(
69+
"com.hashicorp.cdktf-provider-random",
70+
"0.2.64",
71+
),
72+
).resolves.toBe(false);
73+
});
74+
});
75+
});

0 commit comments

Comments
 (0)