Skip to content

Commit

Permalink
Enable nightly build with reproducible comparison
Browse files Browse the repository at this point in the history
Signed-off-by: Sophia Guo <[email protected]>
  • Loading branch information
sophia-guo committed Jun 5, 2023
2 parents bc206dc + 4b70d15 commit bd01565
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 55 deletions.
32 changes: 31 additions & 1 deletion pipelines/build/common/openjdk_build_pipeline.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,33 @@ class Build {
return remoteTargets
}

def compareReproducibleBuild() {
//Currently only enable for jdk17, linux_x64, temurin, nightly, which shouldn't affect current build
//Move out of normal jdk** folder as it won't be regenerated automatically right now
def jobName = "${env.JOB_NAME}"
jobName = jobName.substring(jobName.lastIndexOf('/')+1)
jobName = "${jobName}_reproduce_compare"
if (getJavaVersionNumber().compareTo(17) == 0 &&
buildConfig.ARCHITECTURE.contains('x64') &&
buildConfig.TARGET_OS.contains('linux') &&
buildConfig.VARIANT == 'temurin' &&
!isRelease) {
//For now set the build as independent, no need to wait for result as the build takes time
context.stage('Reproduce Compare') {
def buildParams = params.toString()
// passing buildParams multiline parameter to downstream job, double check the available method
context.build job: jobName,
propagate: false,
parameters: [
context.string(name: 'COMPARED_JOB_NUMBER', value: "${env.BUILD_NUMBER}"),
context.string(name: 'COMPARED_JOB_NAME', value: "${env.JOB_NAME}"),
context.string(name: 'COMPARED_JOB_PARAMS', value: buildParams)
],
wait: false
}
}
}

/*
We use this function at the end of a build to parse a java version string and create a VersionInfo object for deployment in the metadata objects.
E.g. 11.0.9+10-202010192351 would be one example of a matched string.
Expand Down Expand Up @@ -1881,6 +1908,9 @@ class Build {
// Run Smoke Tests and AQA Tests
if (enableTests) {
try {
// Compare reproducible build, using same build pipeline with enableTests = false to avoid recursive building
compareReproducibleBuild()

//Only smoke tests succeed TCK and AQA tests will be triggerred.
if (runSmokeTests() == 'SUCCESS') {
// Remote trigger Eclipse Temurin JCK tests
Expand All @@ -1905,7 +1935,7 @@ class Build {
context.parallel testStages
}
} else {
context.println("[ERROR]Smoke tests are not successful! AQA and Tck tests are blocked ")
context.println('[ERROR]Smoke tests are not successful! AQA and Tck tests are blocked')
}
} catch (Exception e) {
context.println(e.message)
Expand Down
168 changes: 114 additions & 54 deletions tools/reproduce_comparison/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,56 +1,132 @@
/*
Jenkins job does comparison for two different build artifacts
If they are identical, build pass; or fail
Jenkins job does reproduciable build compare.
*/

pipeline {
agent any
parameters {
choice(choices: ['linux', 'mac', 'windows'], name: 'platform', description:'select release on different platform')
string(name: 'URL1', defaultValue: '', description: 'URL to one version of the build artifacts\ne.g.: https://ci.adoptium.net/job/build-scripts/job/openjdk18-pipeline/lastSuccessfulBuild/artifact/target/linux/x64/temurin/OpenJDK18U-jdk_x64_linux_hotspot_2022-06-11-23-30.tar.gz')
string(name: 'URL2', defaultValue: '', description: 'URL to the other version of the build artifacts\ne.g.: https://github.com/adoptium/temurin19-binaries/releases/download/jdk-2022-06-09-19-11-beta/OpenJDK-jdk_x64_linux_hotspot_2022-06-09-03-31.tar.gz')
string(name: 'excludeFiles', defaultValue: 'classes_nocoops.jsa', description: 'File names to be excluded while doing the comparison, separated by `;`')
string(name: 'COMPARED_JOB_NUMBER', defaultValue: '', description: 'Compared nightly build job name')
string(name: 'COMPARED_JOB_NAME', defaultValue: '', description: 'Compared nightly build job number')
string(name: 'COMPARED_JOB_PARAMS', defaultValue: '', description: 'Compared nightly build job parameters')
}

stages {
stage('Lets Compare') {
agent {
label "${params.platform}&&build&&x64" // build label enables comparisons to be done, set to x64 for linux, might need adjust for windows/mac
}
stage('Prepare') { //Copy artifacts, reset parameters,trigger build and copyArtifacts
steps {
cleanWs()
checkout scm
copyArtifacts excludes: '**/OpenJDK*-sbom*metadata.json',
filter: '**/OpenJDK*-jdk*.tar.gz,**/OpenJDK*-sbom*.json',
fingerprintArtifacts: true,
flatten: true,
projectName: "${params.COMPARED_JOB_NAME}",
target: 'original/'
selector: specific("${params.COMPARED_JOB_NUMBER}")
script {
try {
cleanWs()
// use Jenkins crendential to download JDK if source is from openjdkX-pipline
withCredentials([usernamePassword(credentialsId: 'eclipse_temurin_bot_email_and_token', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) {
echo "Fetching artifact1 from ${params.URL1}"
def ret1 = sh returnStatus: true, script: "mkdir url1Dir1 && curl -s --user ${USERNAME}:${PASSWORD} ${params.URL1} | tar -xz -C url1Dir1"
if (ret1 != 0) {
currentBuild.result = 'UNSTABLE'
error 'Stopping after download and uncompress tar file'
}
echo "Fetching artifact2 from ${params.URL2}"
def ret2 = sh returnStatus: true, script: "mkdir url1Dir2 && curl -s --user ${USERNAME}:${PASSWORD} ${params.URL2} | tar -xz -C url1Dir2"
if (ret2 != 0) {
currentBuild.result = 'UNSTABLE'
error 'Stopping after download and uncompress tar file'
def sbomFiles = findFiles(glob: "**/*.json")
def sbomParams = readJSON file: "${sbomFiles[0].name}"
def buildTimeStamp = sbomParams.metadata.timestamp

def sbomProperties = sbomParams.components[0].properties
def reproducedParams = [:]
sbomProperties.each { propertyItem ->
def paramFound = false
propertyItem.each { key, value ->
if (value in ["SCM Ref", "OpenJDK Source Commit", "Temurin Build Ref"]) {
paramFound = true
}
}
// call extra platform specific function
if (params.platform != 'linux') {
"prep${platform}" ('url1Dir1', 'url1Dir2')
if (paramFound) {
reproducedParams.put("${propertyItem.name}", "${propertyItem.value}")
}
def excludeFlags=""
def excludeFileList = excludeFiles.split(';')
for(String excludeFile in excludeFileList) {
excludeFlags="${excludeFlags} --exclude=${excludeFile}"
}

def scmRef = reproducedParams["SCM Ref"]
def buildRef = reproducedParams["Temurin Build Ref"]
buildRef = buildRef.substring(buildRef.lastIndexOf('/')+1)
def scmCommit = reproducedParams["OpenJDK Source Commit"]
scmCommit = scmCommit.substring(scmCommit.lastIndexOf('/')+1)

//convert COMPARED_JOB_PARAMS to json formatt string
def jobParams = COMPARED_JOB_PARAMS.replaceAll("=", ':')
jobParams = jobParams.replace("ADOPT_DEFAULTS_JSON", "ADOPT_DEFAULTS_TEMP")
jobParams = jobParams.replace("DEFAULTS_JSON", "\"DEFAULTS_JSON\"")
jobParams = jobParams.replace("USER_REMOTE_CONFIGS", "\"USER_REMOTE_CONFIGS\"")
jobParams = jobParams.replace("ADOPT_DEFAULTS_TEMP", "\"ADOPT_DEFAULTS_JSON\"")
jobParams = jobParams.replace("BUILD_CONFIGURATION", "\"BUILD_CONFIGURATION\"")

def jsonJobParams = new groovy.json.JsonSlurper().parseText(jobParams)
jsonJobParams.BUILD_CONFIGURATION.SCM_REF = scmRef
jsonJobParams.BUILD_CONFIGURATION.BUILD_REF = buildRef
jsonJobParams.BUILD_CONFIGURATION.BUILD_ARGS += " --build-reproducible-date ${buildTimeStamp}" //'${buildTimeStamp}'
jsonJobParams.BUILD_CONFIGURATION.ENABLE_TESTS = "false"

def buildParams = [
text(name: 'BUILD_CONFIGURATION', value: "${jsonJobParams.BUILD_CONFIGURATION}"),
text(name: 'USER_REMOTE_CONFIGS', value: "${jsonJobParams.USER_REMOTE_CONFIGS}"),
text(name: 'DEFAULTS_JSON', value: "${jsonJobParams.DEFAULTS_JSON}"),
text(name: 'ADOPT_DEFAULTS_JSON', value: "${jsonJobParams.ADOPT_DEFAULTS_JSON}")
]
def reproduciableJob = build job: "${COMPARED_JOB_NAME}",
propagate: false,
parameters: buildParams

def result = reproduciableJob.getResult()
if (result == 'SUCCESS') {
try {
timeout(time: 2, unit: 'HOURS') {
copyArtifacts(
projectName:"${COMPARED_JOB_NAME}",
selector:specific("${reproduciableJob.getNumber()}"),
filter: "**/*.tar.gz",
target: 'reproduced/',
fingerprintArtifacts: true,
flatten: true
)
}
} catch (Exception e) {
echo "Cannot run copyArtifacts from job ${COMPARED_JOB_NAME}. Exception: ${e.message}. Skipping copyArtifacts..."
}
def retVal = sh returnStatus: true, script: "diff -q -r url1Dir1 url1Dir2 ${excludeFlags}"
if (retVal != 0) {
currentBuild.result = 'FAILURE'
error 'Error: two builds are not the same!'
} else {
echo 'Success: two builds are the same!'
} else {
error "Reproduced build failed, exit comparision"
return
}

def originalJDKFile = findFiles(glob: "original/*.tar.gz")
def reproducedJDKFile = findFiles(glob: "reproduced/*.tar.gz")
def result1 = sh returnStatus: true, script: "tar -xz ${originalJDKFile[0].name} -C original"
def result2 = sh returnStatus: true, script: "tar -xz ${reproducedJDKFile[0].name} -C reproduced"
if (result1 != 0 || result2 != 0) {
currentBuild.result = 'UNSTABLE'
error 'Wrong jdk tar files'
return
}
}
}
}
stage('Compare') {
steps {
checkout([$class: 'GitSCM', branches: [[name: 'master']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "temurin-build"]], submoduleCfg: [], userRemoteConfigs: [[url: "https://github.com/adoptium/temurin-build.git"]]])
script {
try {
dir('temurin-build/tools/reproduce_comparison') {
def rc = 0
if (COMPARED_JOB_NAME.contains('linux')) {
sh "chmod 776 ./tooling/linux_repo_compare.sh"
rc = sh returnStatus: true, script: "./tooling/linux_repo_compare.sh temurin ./original temurin ./reproduced"
} else if (COMPARED_JOB_NAME.contains('mac')) {
// mac
//sh "chmod 776 ./tooling/mac_repo_compare.sh"
//sh "./tooling/mac_repo_compare.sh temurin ./original temurin ./reproduced ${self_cert_file} ${self_cert_passwd}"
} else if (COMPARED_JOB_NAME.contains('windows')) {
//windows
}
if (rc != 0) {
currentBuild.result = 'FAILURE'
error 'Error: two builds are not the same!'
} else {
echo 'Success: two builds are the same!'
}
}
} catch (Exception err) {
echo err.getMessage()
Expand All @@ -63,19 +139,3 @@ pipeline {
}
}
}

/* TODO:
set correct defaultpath for localCert
see: https://github.com/adoptium/temurin-build/issues/3015#issuecomment-1175322612
*/
def prepmac(String jdk1, String jdk2, String localCert='defaultpath') {
sh "chmod 776 ${WORKSPACE}/tools/reproduce_comparison/compareMacOS.sh"
sh "${WORKSPACE}/tools/reproduce_comparison/compareMacOS.sh $jdk1 $jdk2 $localCert skip"
}

/*
TODO: placeholder for windows comparison
def prepwindows(String jdk1, String jdk2, ...) {
sh "./tools/reproduce_comparison/compareWindows.sh ..."
}
*/
81 changes: 81 additions & 0 deletions tools/reproduce_comparison/JenkinsfileOld
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
Jenkins job does comparison for two different build artifacts
If they are identical, build pass; or fail
*/

pipeline {
agent any
parameters {
choice(choices: ['linux', 'mac', 'windows'], name: 'platform', description:'select release on different platform')
string(name: 'URL1', defaultValue: '', description: 'URL to one version of the build artifacts\ne.g.: https://ci.adoptium.net/job/build-scripts/job/openjdk18-pipeline/lastSuccessfulBuild/artifact/target/linux/x64/temurin/OpenJDK18U-jdk_x64_linux_hotspot_2022-06-11-23-30.tar.gz')
string(name: 'URL2', defaultValue: '', description: 'URL to the other version of the build artifacts\ne.g.: https://github.com/adoptium/temurin19-binaries/releases/download/jdk-2022-06-09-19-11-beta/OpenJDK-jdk_x64_linux_hotspot_2022-06-09-03-31.tar.gz')
string(name: 'excludeFiles', defaultValue: 'classes_nocoops.jsa', description: 'File names to be excluded while doing the comparison, separated by `;`')
}

stages {
stage('Lets Compare') {
agent {
label "${params.platform}&&build&&x64" // build label enables comparisons to be done, set to x64 for linux, might need adjust for windows/mac
}
steps {
script {
try {
cleanWs()
// use Jenkins crendential to download JDK if source is from openjdkX-pipline
withCredentials([usernamePassword(credentialsId: 'eclipse_temurin_bot_email_and_token', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) {
echo "Fetching artifact1 from ${params.URL1}"
def ret1 = sh returnStatus: true, script: "mkdir url1Dir1 && curl -s --user ${USERNAME}:${PASSWORD} ${params.URL1} | tar -xz -C url1Dir1"
if (ret1 != 0) {
currentBuild.result = 'UNSTABLE'
error 'Stopping after download and uncompress tar file'
}
echo "Fetching artifact2 from ${params.URL2}"
def ret2 = sh returnStatus: true, script: "mkdir url1Dir2 && curl -s --user ${USERNAME}:${PASSWORD} ${params.URL2} | tar -xz -C url1Dir2"
if (ret2 != 0) {
currentBuild.result = 'UNSTABLE'
error 'Stopping after download and uncompress tar file'
}
}
// call extra platform specific function
if (params.platform != 'linux') {
"prep${platform}" ('url1Dir1', 'url1Dir2')
}
def excludeFlags=""
def excludeFileList = excludeFiles.split(';')
for(String excludeFile in excludeFileList) {
excludeFlags="${excludeFlags} --exclude=${excludeFile}"
}
def retVal = sh returnStatus: true, script: "diff -q -r url1Dir1 url1Dir2 ${excludeFlags}"
if (retVal != 0) {
currentBuild.result = 'FAILURE'
error 'Error: two builds are not the same!'
} else {
echo 'Success: two builds are the same!'
}
} catch (Exception err) {
echo err.getMessage()
currentBuild.result = 'FAILURE'
} finally {
cleanWs()
}
}
}
}
}
}

/* TODO:
set correct defaultpath for localCert
see: https://github.com/adoptium/temurin-build/issues/3015#issuecomment-1175322612
*/
def prepmac(String jdk1, String jdk2, String localCert='defaultpath') {
sh "chmod 776 ${WORKSPACE}/tools/reproduce_comparison/compareMacOS.sh"
sh "${WORKSPACE}/tools/reproduce_comparison/compareMacOS.sh $jdk1 $jdk2 $localCert skip"
}

/*
TODO: placeholder for windows comparison
def prepwindows(String jdk1, String jdk2, ...) {
sh "./tools/reproduce_comparison/compareWindows.sh ..."
}
*/

0 comments on commit bd01565

Please sign in to comment.