Skip to content

fix: stop publishing a bigtable-beam-import pom with no dependencies - #4632

Merged
mutianf merged 2 commits into
googleapis:mainfrom
mutianf:fix-beam-import-published-pom
Sep 3, 2026
Merged

fix: stop publishing a bigtable-beam-import pom with no dependencies#4632
mutianf merged 2 commits into
googleapis:mainfrom
mutianf:fix-beam-import-published-pom

Conversation

@mutianf

@mutianf mutianf commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Problem

bigtable-beam-import 2.20.0 and 2.20.1 were published to Maven Central with zero compile dependencies in their poms.

The module attaches its shaded jar under a classifier (shadedArtifactAttached=true), so the main artifact is the thin, unshaded jar — it needs every one of its compile dependencies declared. maven-shade-plugin strips them from the generated dependency-reduced pom anyway in that configuration (MSHADE-419), which is not fixed in 3.6.0, despite what the comment in the root pom claimed. The bump to 3.6.0 in #4626 flipped the module onto that code path.

Consumers resolving the artifact from a repository get nothing transitively. GoogleCloudPlatform/DataflowTemplates hits this staging the Cloud_Bigtable_to_GCS_SequenceFile template:

ClassNotFoundException: org.apache.hadoop.hbase.io.ImmutableBytesWritable

Every one of these disappeared from the published pom:

bigtable-hbase-beam, beam-sdks-java-core, beam-sdks-java-io-hadoop-format, beam-runners-google-cloud-dataflow-java, hadoop-client-api, hadoop-client-runtime, hbase-shaded-mapreduce, gcs-connector

Why CI didn't catch it

The Maven reactor resolves sibling modules from their source poms and never reads dependency-reduced-pom.xml, so the broken pom is invisible in-repo. There is no in-repo consumer of the published artifact, and neither dependencies.sh nor linkage-monitor inspects the deployed pom.

Changes

  1. bigtable-beam-import/pom.xml — set <createDependencyReducedPom>false</createDependencyReducedPom> explicitly, rather than relying on a plugin default whose behavior changed between 3.2.4 and 3.6.0.

  2. New verify-published-pom-deps goal in bigtable-build-helper, wired into the module's verify phase. It reads project.getFile() — which shade rewrites to the reduced pom when enabled — so it inspects exactly what would be deployed, and fails the build listing any missing required compile dependency plus a pointer to MSHADE-419. Two integration tests cover the healthy pom and the stripped one.

  3. Root pom.xml / renovate.json5 — corrected the comment that asserted 3.6.0 was unaffected, and dropped the renovate rule pinning maven-shade-plugin away from 3.3.0. That rule described the bug incompletely (it isn't specific to 3.3.0), and correcting the range would have banned the 3.6.0 we need for the Java 17 multi-release classes; the explicit flag plus the guard replaces it.

Verification

Check Result
bigtable-build-helper integration tests Passed: 8, Failed: 0, Errors: 0, Skipped: 0
mvn -pl bigtable-dataflow-parent/bigtable-beam-import -am verify BUILD SUCCESS; guard silent; no dependency-reduced-pom.xml generated
Same, with createDependencyReducedPom flipped back to true BUILD FAILURE; guard reports all 8 deps missing

The negative run reproduces the shipped defect exactly — the same eight artifacts that vanished for DataflowTemplates.

bigtable-beam-import attaches its shaded jar under a classifier, so the
main artifact is the thin, unshaded jar and its pom must keep every
compile dependency. maven-shade-plugin still strips them from the
generated dependency-reduced pom in that configuration (MSHADE-419),
which is not fixed in 3.6.0 despite what the comment in the root pom
claimed. The bump to 3.6.0 in googleapis#4626 therefore published 2.20.0 and
2.20.1 poms declaring zero compile dependencies.

Consumers resolving the artifact from Maven Central get no transitive
deps at all. GoogleCloudPlatform/DataflowTemplates hits this staging the
Cloud_Bigtable_to_GCS_SequenceFile template, which fails with
ClassNotFoundException: org.apache.hadoop.hbase.io.ImmutableBytesWritable.
Nothing caught it in-repo because the reactor resolves sibling modules
from source poms and never reads dependency-reduced-pom.xml.

- set createDependencyReducedPom=false explicitly on the module rather
  than relying on a plugin default that changed between 3.2.4 and 3.6.0
- add a verify-published-pom-deps goal to bigtable-build-helper that
  inspects the pom that would actually be deployed and fails the build
  if a required compile dependency is missing, wired into the module's
  verify phase; covered by integration tests for both the healthy and
  the MSHADE-419-stripped case
- correct the root pom comment and drop the renovate rule that pinned
  maven-shade-plugin away from 3.3.0, which was both an incomplete
  description of the bug and moot now that the flag is explicit

Change-Id: I2d269c60175e10da7e5b361f410dd06f7861ba48
@mutianf
mutianf requested a review from a team as a code owner September 3, 2026 16:10
@product-auto-label product-auto-label Bot added size: l Pull request size is large. api: bigtable Issues related to the googleapis/java-bigtable-hbase API. labels Sep 3, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new Maven plugin goal, verify-published-pom-deps, to guard against the maven-shade-plugin stripping required compile dependencies from the published POM (as seen in MSHADE-419). It also adds integration tests to verify this behavior. The review feedback suggests improving the robustness of the new Mojo by adding null and validity checks for the POM file and dependency coordinates, and explicitly specifying UTF-8 encoding when reading the build log in the integration test script.

Comment on lines +71 to +72
File publishedPom = project.getFile();
Set<String> declared = readPropagatingDependencies(publishedPom);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If project.getFile() returns null or a non-existent file (which can happen in certain test environments or synthetic projects), calling readPropagatingDependencies will result in a NullPointerException when opening the FileInputStream. It is safer to check if the file is non-null and is a valid file before proceeding.

    File publishedPom = project.getFile();
    if (publishedPom == null || !publishedPom.isFile()) {
      throw new MojoFailureException("Published POM file is not available or invalid: " + publishedPom);
    }
    Set<String> declared = readPropagatingDependencies(publishedPom);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 23d3938. Failing loudly beats an NPE here, and more importantly a build that cannot read the pom must not look like it passed the check.

Comment on lines +112 to +119
for (Dependency dependency : model.getDependencies()) {
String scope = dependency.getScope();
// An absent scope means compile.
if (scope != null && !"compile".equals(scope) && !"runtime".equals(scope)) {
continue;
}
declared.add(resolveGroupId(dependency.getGroupId()) + ":" + dependency.getArtifactId());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If the parsed POM is malformed or missing required fields, dependency.getGroupId() or dependency.getArtifactId() could return null. Adding a null check prevents adding invalid "null:artifactId" or "groupId:null" entries to the declared set.

    for (Dependency dependency : model.getDependencies()) {
      String scope = dependency.getScope();
      // An absent scope means compile.
      if (scope != null && !"compile".equals(scope) && !"runtime".equals(scope)) {
        continue;
      }
      String groupId = dependency.getGroupId();
      String artifactId = dependency.getArtifactId();
      if (groupId != null && artifactId != null) {
        declared.add(resolveGroupId(groupId) + ":" + artifactId);
      }
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 23d3938. A half-declared coordinate cannot satisfy a requirement anyway, so skipping it lets the dependency it was meant to be get reported as missing, which is the outcome we want.

import java.nio.file.Files;

File buildLog = new File(basedir, "build.log");
String content = new String(Files.readAllBytes(buildLog.toPath()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Reading the file bytes into a String without specifying a charset uses the platform's default encoding. This can lead to flaky or failing tests on environments (like Windows runners in CI) where the default encoding is not UTF-8. It is safer to explicitly specify StandardCharsets.UTF_8.

String content = new String(Files.readAllBytes(buildLog.toPath()), java.nio.charset.StandardCharsets.UTF_8);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipping this one. Both assertion strings are pure ASCII, which decodes identically under any charset this would realistically run with, so there is no flakiness to fix. The three existing verify.bsh files in this module (verify-shaded-jar-entries-leak, verify-shaded-exclusions-unpromoted, verify-mirror-deps-misaligned) all read the build log the same way, and making only the new one differ costs consistency for no behavior change. Worth doing as a sweep across all four if we ever want it.

…-pom-deps

Change-Id: Ice5b8a44978b4833bc48c5fe037a263bbce68b77
@mutianf mutianf added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Sep 3, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Sep 3, 2026
@mutianf
mutianf merged commit be127de into googleapis:main Sep 3, 2026
17 of 18 checks passed
@mutianf
mutianf deleted the fix-beam-import-published-pom branch September 3, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: bigtable Issues related to the googleapis/java-bigtable-hbase API. size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants