Skip to content

Commit 2dc071f

Browse files
authored
Detect breaking changes on pull requests (#12974)
1 parent c25b00c commit 2dc071f

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
@@ -109,6 +109,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
109109
- Allow setting KEYSTORE_PASSWORD through env variable ([#12865](https://github.com/opensearch-project/OpenSearch/pull/12865))
110110
- [Concurrent Segment Search] Perform buildAggregation concurrently and support Composite Aggregations ([#12697](https://github.com/opensearch-project/OpenSearch/pull/12697))
111111
- [Concurrent Segment Search] Disable concurrent segment search for system indices and throttled requests ([#12954](https://github.com/opensearch-project/OpenSearch/pull/12954))
112+
- Detect breaking changes on pull requests ([#9044](https://github.com/opensearch-project/OpenSearch/pull/9044))
112113

113114
### Dependencies
114115
- 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 {
@@ -378,3 +379,81 @@ tasks.named("sourcesJar").configure {
378379
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
379380
}
380381
}
382+
383+
/** Compares the current build against a snapshot build */
384+
tasks.register("japicmp", me.champeau.gradle.japicmp.JapicmpTask) {
385+
oldClasspath.from(files("${buildDir}/snapshot/opensearch-${version}.jar"))
386+
newClasspath.from(tasks.named('jar'))
387+
onlyModified = true
388+
failOnModification = true
389+
ignoreMissingClasses = true
390+
annotationIncludes = ['@org.opensearch.common.annotation.PublicApi']
391+
txtOutputFile = layout.buildDirectory.file("reports/java-compatibility/report.txt")
392+
htmlOutputFile = layout.buildDirectory.file("reports/java-compatibility/report.html")
393+
dependsOn downloadSnapshot
394+
}
395+
396+
/** If the Java API Comparison task failed, print a hint if the change should be merged from its target branch */
397+
gradle.taskGraph.afterTask { Task task, TaskState state ->
398+
if (task.name == 'japicmp' && state.failure != null) {
399+
def sha = getGitShaFromJar("${buildDir}/snapshot/opensearch-${version}.jar")
400+
logger.info("Incompatiable java api from snapshot jar built off of commit ${sha}")
401+
402+
if (!inHistory(sha)) {
403+
logger.warn('\u001B[33mPlease merge from the target branch and run this task again.\u001B[0m')
404+
}
405+
}
406+
}
407+
408+
/** Downloads latest snapshot from maven repository */
409+
tasks.register("downloadSnapshot", Copy) {
410+
def mavenSnapshotRepoUrl = "https://aws.oss.sonatype.org/content/repositories/snapshots/"
411+
def groupId = "org.opensearch"
412+
def artifactId = "opensearch"
413+
414+
repositories {
415+
maven {
416+
url mavenSnapshotRepoUrl
417+
}
418+
}
419+
420+
configurations {
421+
snapshotArtifact
422+
}
423+
424+
dependencies {
425+
snapshotArtifact("${groupId}:${artifactId}:${version}:")
426+
}
427+
428+
from configurations.snapshotArtifact
429+
into "$buildDir/snapshot"
430+
}
431+
432+
/** Check if the sha is in the current history */
433+
def inHistory(String sha) {
434+
try {
435+
def commandCheckSha = "git merge-base --is-ancestor ${sha} HEAD"
436+
commandCheckSha.execute()
437+
return true
438+
} catch (Exception) {
439+
return false
440+
}
441+
}
442+
443+
/** Extracts the Git SHA used to build a jar from its manifest */
444+
def getGitShaFromJar(String jarPath) {
445+
def sha = ''
446+
try {
447+
// Open the JAR file
448+
def jarFile = new java.util.jar.JarFile(jarPath)
449+
// Get the manifest from the JAR file
450+
def manifest = jarFile.manifest
451+
def attributes = manifest.mainAttributes
452+
// Assuming the Git SHA is stored under an attribute named 'Git-SHA'
453+
sha = attributes.getValue('Change')
454+
jarFile.close()
455+
} catch (IOException e) {
456+
println "Failed to read the JAR file: $e.message"
457+
}
458+
return sha
459+
}

0 commit comments

Comments
 (0)