Skip to content

Commit 1577687

Browse files
Detect breaking changes on pull requests (#12974)
(cherry picked from commit 2dc071f) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent f69da6c commit 1577687

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: "Detect Breaking Changes"
2+
on: [push, pull_request]
3+
jobs:
4+
detect-breaking-change:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
- uses: actions/setup-java@v4
9+
with:
10+
distribution: temurin # Temurin is a distribution of adoptium
11+
java-version: 21
12+
- uses: gradle/gradle-build-action@v3
13+
with:
14+
arguments: japicmp
15+
gradle-version: 8.7
16+
build-root-directory: server
17+
- if: failure()
18+
run: cat server/build/reports/java-compatibility/report.txt
19+
- if: failure()
20+
uses: actions/upload-artifact@v4
21+
with:
22+
name: java-compatibility-report.html
23+
path: ${{ github.workspace }}/server/build/reports/java-compatibility/report.html
24+

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1111
- Allow setting KEYSTORE_PASSWORD through env variable ([#12865](https://github.com/opensearch-project/OpenSearch/pull/12865))
1212
- Add a counter to node stat (and _cat/shards) api to track shard going from idle to non-idle ([#12768](https://github.com/opensearch-project/OpenSearch/pull/12768))
1313
- [Concurrent Segment Search] Disable concurrent segment search for system indices and throttled requests ([#12954](https://github.com/opensearch-project/OpenSearch/pull/12954))
14+
- Detect breaking changes on pull requests ([#9044](https://github.com/opensearch-project/OpenSearch/pull/9044))
1415

1516
### Dependencies
1617
- Bump `org.apache.commons:commons-configuration2` from 2.10.0 to 2.10.1 ([#12896](https://github.com/opensearch-project/OpenSearch/pull/12896))

server/build.gradle

+79
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ plugins {
3636
id('opensearch.publish')
3737
id('opensearch.internal-cluster-test')
3838
id('opensearch.optional-dependencies')
39+
id('me.champeau.gradle.japicmp') version '0.4.2'
3940
}
4041

4142
publishing {
@@ -414,3 +415,81 @@ tasks.named("sourcesJar").configure {
414415
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
415416
}
416417
}
418+
419+
/** Compares the current build against a snapshot build */
420+
tasks.register("japicmp", me.champeau.gradle.japicmp.JapicmpTask) {
421+
oldClasspath.from(files("${buildDir}/snapshot/opensearch-${version}.jar"))
422+
newClasspath.from(tasks.named('jar'))
423+
onlyModified = true
424+
failOnModification = true
425+
ignoreMissingClasses = true
426+
annotationIncludes = ['@org.opensearch.common.annotation.PublicApi']
427+
txtOutputFile = layout.buildDirectory.file("reports/java-compatibility/report.txt")
428+
htmlOutputFile = layout.buildDirectory.file("reports/java-compatibility/report.html")
429+
dependsOn downloadSnapshot
430+
}
431+
432+
/** If the Java API Comparison task failed, print a hint if the change should be merged from its target branch */
433+
gradle.taskGraph.afterTask { Task task, TaskState state ->
434+
if (task.name == 'japicmp' && state.failure != null) {
435+
def sha = getGitShaFromJar("${buildDir}/snapshot/opensearch-${version}.jar")
436+
logger.info("Incompatiable java api from snapshot jar built off of commit ${sha}")
437+
438+
if (!inHistory(sha)) {
439+
logger.warn('\u001B[33mPlease merge from the target branch and run this task again.\u001B[0m')
440+
}
441+
}
442+
}
443+
444+
/** Downloads latest snapshot from maven repository */
445+
tasks.register("downloadSnapshot", Copy) {
446+
def mavenSnapshotRepoUrl = "https://aws.oss.sonatype.org/content/repositories/snapshots/"
447+
def groupId = "org.opensearch"
448+
def artifactId = "opensearch"
449+
450+
repositories {
451+
maven {
452+
url mavenSnapshotRepoUrl
453+
}
454+
}
455+
456+
configurations {
457+
snapshotArtifact
458+
}
459+
460+
dependencies {
461+
snapshotArtifact("${groupId}:${artifactId}:${version}:")
462+
}
463+
464+
from configurations.snapshotArtifact
465+
into "$buildDir/snapshot"
466+
}
467+
468+
/** Check if the sha is in the current history */
469+
def inHistory(String sha) {
470+
try {
471+
def commandCheckSha = "git merge-base --is-ancestor ${sha} HEAD"
472+
commandCheckSha.execute()
473+
return true
474+
} catch (Exception) {
475+
return false
476+
}
477+
}
478+
479+
/** Extracts the Git SHA used to build a jar from its manifest */
480+
def getGitShaFromJar(String jarPath) {
481+
def sha = ''
482+
try {
483+
// Open the JAR file
484+
def jarFile = new java.util.jar.JarFile(jarPath)
485+
// Get the manifest from the JAR file
486+
def manifest = jarFile.manifest
487+
def attributes = manifest.mainAttributes
488+
// Assuming the Git SHA is stored under an attribute named 'Git-SHA'
489+
sha = attributes.getValue('Change')
490+
jarFile.close()
491+
} catch (IOException e) {
492+
println "Failed to read the JAR file: $e.message"
493+
}
494+
return sha
495+
}

0 commit comments

Comments
 (0)