split groovy scripts more, use job parameters

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2018-08-21 11:17:25 -04:00
parent 68e4be9197
commit 5fb33cc461
No known key found for this signature in database
GPG Key ID: 4EF064D0E6D63020
10 changed files with 211 additions and 185 deletions

View File

@ -5,7 +5,7 @@ pipeline {
buildDiscarder(logRotator( buildDiscarder(logRotator(
numToKeepStr: '10', numToKeepStr: '10',
daysToKeepStr: '30', daysToKeepStr: '30',
artifactNumToKeepStr: '4', artifactNumToKeepStr: '6',
)) ))
} }
@ -25,9 +25,10 @@ pipeline {
stage('Prep') { stage('Prep') {
steps { steps {
script { script {
print "Running ${params.BUILD_TYPE} build!"
/* Necessary to load methods */ /* Necessary to load methods */
mobile = load 'ci/mobile.groovy' mobile = load 'ci/mobile.groovy'
mobile.prepDeps() mobile.prep(type: params.BUILD_TYPE)
} }
} }
} }
@ -43,25 +44,25 @@ pipeline {
} }
stage('Compile') { stage('Compile') {
steps { steps {
script { apk = mobile.compileAndroid(e2e: params.RUN_E2E) } script { apk = mobile.android.compile(type: params.BUILD_TYPE) }
}
}
stage('Bundle') {
when { expression { !params.RUN_E2E } }
steps {
script { mobile.bundleAndroid() }
} }
} }
stage('Archive') { stage('Archive') {
when { expression { !params.RUN_E2E } }
steps { steps {
script { archiveArtifacts apk } script { archiveArtifacts apk }
} }
} }
stage('Upload') {
when { expression { params.BUILD_TYPE == 'release' } }
steps {
script { mobile.android.uploadToPlayStore() }
}
}
stage('Run e2e') { stage('Run e2e') {
when { expression { params.RUN_E2E } } when { expression { params.BUILD_TYPE == 'e2e' } }
steps { script { steps { script {
apk = mobile.uploadSauceLabs() mobile.android.uploadToDiawi()
apk = mobile.android.uploadToSauceLabs()
build( build(
job: 'end-to-end-tests/status-app-nightly', wait: false, job: 'end-to-end-tests/status-app-nightly', wait: false,
parameters: [string(name: 'apk', value: "--apk=${apk}")] parameters: [string(name: 'apk', value: "--apk=${apk}")]

View File

@ -14,69 +14,49 @@ pipeline {
stage('Tag') { stage('Tag') {
steps { script { steps { script {
common = load('ci/common.groovy') common = load('ci/common.groovy')
/* to avoid race conditions in parallel builds */ /* to avoid missing build tag parallel builds */
print "Build Number: ${common.tagBuild()}" print "Build Number: ${common.tagBuild()}"
} } } }
} }
stage('Build') { stage('Build') {
parallel { parallel {
stage('MacOS') { stage('MacOS') { steps { script {
steps { script { osx = common.buildBranch('status-react/combined/desktop-macos')
osx = build('status-react/combined/desktop-macos') } } }
} } stage('Linux') { steps { script {
} nix = common.buildBranch('status-react/combined/desktop-linux')
stage('Linux') { } } }
steps { script { stage('iOS') { steps { script {
nix = build('status-react/combined/desktop-linux') ios = common.buildBranch('status-react/combined/mobile-ios')
} } } } }
} stage('Android') { steps { script {
stage('iOS') { dro = common.buildBranch('status-react/combined/mobile-android')
steps { script { } } }
ios = build('status-react/combined/mobile-ios') stage('Android e2e') { steps { script {
} } e2e = common.buildBranch('status-react/combined/mobile-android', 'e2e')
} } } }
stage('Android') {
steps { script {
dro = build('status-react/combined/mobile-android')
} }
}
stage('Android e2e') {
steps {
build('status-react/combined/mobile-android-e2e')
}
}
} }
} }
stage('Archive') { stage('Archive') {
steps { steps { script {
sh('rm -f pkg/*') sh('rm -f pkg/*')
copyArtifacts( common.copyArts('status-react/combined/desktop-macos', osx.number)
projectName: 'status-react/combined/mobile-ios', target: 'pkg', common.copyArts('status-react/combined/desktop-linux', nix.number)
flatten: true, selector: specific("${ios.number}") common.copyArts('status-react/combined/mobile-android', dro.number)
) common.copyArts('status-react/combined/mobile-android', e2e.number)
copyArtifacts(
projectName: 'status-react/combined/mobile-android', target: 'pkg',
flatten: true, selector: specific("${dro.number}")
)
copyArtifacts(
projectName: 'status-react/combined/desktop-macos', target: 'pkg',
flatten: true, selector: specific("${osx.number}")
)
copyArtifacts(
projectName: 'status-react/combined/desktop-linux', target: 'pkg',
flatten: true, selector: specific("${nix.number}")
)
archiveArtifacts('pkg/*') archiveArtifacts('pkg/*')
} } }
} }
stage('Upload') { stage('Upload') {
when { expression { params.PUBLISH } } when { expression { params.BUILD_TYPE == 'nightly' } }
steps { script { steps { script {
def pkg = "StatusIm-${GIT_COMMIT.take(6)}" def pkg = "pkg/StatusIm-${GIT_COMMIT.take(6)}"
apkUrl = common.uploadArtifact('pkg', "${pkg}.apk") e2eUrl = common.uploadArtifact("${pkg}-e2e.apk")
ipaUrl = common.uploadArtifact('pkg', "${pkg}.ipa") apkUrl = common.uploadArtifact("${pkg}.apk")
dmgUrl = common.uploadArtifact('pkg', "${pkg}.dmg") dmgUrl = common.uploadArtifact("${pkg}.dmg")
appUrl = common.uploadArtifact('pkg', "${pkg}.AppImage") appUrl = common.uploadArtifact("${pkg}.AppImage")
/* special case for iOS Diawi link */
ipaUrl = ios.getEnvironment().DIAWI_URL
} } } }
} }
stage('Notify') { stage('Notify') {
@ -86,7 +66,8 @@ pipeline {
"Build success! "+ "Build success! "+
"<${currentBuild.absoluteUrl}|${currentBuild.displayName}> "+ "<${currentBuild.absoluteUrl}|${currentBuild.displayName}> "+
"(${currentBuild.durationString})\n"+ "(${currentBuild.durationString})\n"+
(params.PUBLISH ? (params.BUILD_TYPE == 'nightly' ?
"E2E: ${e2eUrl}\n"+
"APK: ${apkUrl}\n"+ "APK: ${apkUrl}\n"+
"IPA: ${ipaUrl}\n"+ "IPA: ${ipaUrl}\n"+
"DMG: ${dmgUrl}\n"+ "DMG: ${dmgUrl}\n"+
@ -98,7 +79,7 @@ pipeline {
} }
} }
stage('Publish') { stage('Publish') {
when { expression { params.PUBLISH } } when { expression { params.BUILD_TYPE == 'nightly' } }
steps { steps {
build( build(
job: 'misc/status-im.github.io-update_env', job: 'misc/status-im.github.io-update_env',

View File

@ -29,9 +29,10 @@ pipeline {
stage('Prep') { stage('Prep') {
steps { steps {
script { script {
print "Running ${params.BUILD_TYPE} build!"
/* Necessary to load methods */ /* Necessary to load methods */
mobile = load 'ci/mobile.groovy' mobile = load 'ci/mobile.groovy'
mobile.prepDeps() mobile.prep(type: params.BUILD_TYPE)
} }
} }
} }
@ -47,7 +48,7 @@ pipeline {
} }
stage('Compile') { stage('Compile') {
steps { steps {
script { api = mobile.compileiOS() } script { api = mobile.ios.compile(type: params.BUILD_TYPE) }
} }
} }
stage('Archive') { stage('Archive') {
@ -56,9 +57,8 @@ pipeline {
} }
} }
stage('Upload') { stage('Upload') {
when { expression { params.RUN_E2E } }
steps { steps {
script { env.DIAWI_URL = uploadiOS() } script { env.DIAWI_URL = mobile.ios.uploadToDiawi() }
} }
} }
} }

66
ci/android.groovy Normal file
View File

@ -0,0 +1,66 @@
common = load 'ci/common.groovy'
def uploadArtifact() {
def artifact_dir = pwd() + '/android/app/build/outputs/apk/release/'
println (artifact_dir + 'app-release.apk')
def artifact = (artifact_dir + 'app-release.apk')
def server = Artifactory.server('artifacts')
def filename = "im.status.ethereum-${GIT_COMMIT.take(6)}-n-fl.apk"
def newArtifact = (artifact_dir + filename)
sh "cp ${artifact} ${newArtifact}"
def uploadSpec = '{ "files": [ { "pattern": "*apk/release/' + filename + '", "target": "nightlies-local" }]}'
def buildInfo = server.upload(uploadSpec)
return 'http://artifacts.status.im:8081/artifactory/nightlies-local/' + filename
}
def compile(type = 'nightly') {
common.tagBuild()
def gradleOpt = ''
if (type == 'release') {
gradleOpt = "-PreleaseVersion=${common.version()}"
}
dir('android') {
sh './gradlew react-native-android:installArchives'
sh "./gradlew assembleRelease ${gradleOpt}"
}
def pkg = "StatusIm-${GIT_COMMIT.take(6)}${(type = 'e2e' ?: '-e2e')}.apk"
sh "cp android/app/build/outputs/apk/release/app-release.apk ${pkg}"
return pkg
}
def uploadToPlayStore() {
withCredentials([
string(credentialsId: "SUPPLY_JSON_KEY_DATA", variable: 'GOOGLE_PLAY_JSON_KEY'),
string(credentialsId: "SLACK_URL", variable: 'SLACK_URL')
]) {
sh 'bundle exec fastlane android nightly'
}
}
def uploadToSauceLabs() {
env.SAUCE_LABS_APK = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
withCredentials([
string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'),
string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'),
string(credentialsId: 'GIT_HUB_TOKEN', variable: 'GITHUB_TOKEN'),
string(credentialsId: 'SLACK_JENKINS_WEBHOOK', variable: 'SLACK_URL')
]) {
sh 'fastlane android saucelabs'
}
return env.SAUCE_LABS_APK
}
def uploadToDiawi() {
env.SAUCE_LABS_APK = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
withCredentials([
string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'),
string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'),
string(credentialsId: 'GIT_HUB_TOKEN', variable: 'GITHUB_TOKEN'),
string(credentialsId: 'SLACK_JENKINS_WEBHOOK', variable: 'SLACK_URL')
]) {
sh 'fastlane android saucelabs'
}
return env.SAUCE_LABS_APK
}
return this

View File

@ -1,3 +1,26 @@
def version() {
return readFile("${env.WORKSPACE}/VERSION").trim()
}
def buildBranch(name = null, buildType = params.BUILD_TYPE) {
/* always pass the BRANCH and BUILD_TYPE params with current branch */
return build(
job: name,
parameters: [
[name: 'BRANCH', value: env.GIT_BRANCH, $class: 'StringParameterValue'],
[name: 'BUILD_TYPE', value: buildType, $class: 'StringParameterValue'],
])
}
def copyArts(projectName, buildNo) {
copyArtifacts(
projectName: projectName,
target: 'pkg',
flatten: true,
selector: specific("${buildNo}")
)
}
def installJSDeps(platform) { def installJSDeps(platform) {
def attempt = 1 def attempt = 1
def maxAttempts = 10 def maxAttempts = 10

View File

@ -13,6 +13,12 @@ external_modules_dir = [
'modules/react-native-status/desktop', 'modules/react-native-status/desktop',
] ]
external_fonts = [
'../../../../../resources/fonts/SF-Pro-Text-Regular.otf',
'../../../../../resources/fonts/SF-Pro-Text-Medium.otf',
'../../../../../resources/fonts/SF-Pro-Text-Light.otf',
]
def cleanupBuild() { def cleanupBuild() {
sh """ sh """
rm -rf \\ rm -rf \\
@ -88,6 +94,7 @@ def compileLinux() {
cmake -Wno-dev \\ cmake -Wno-dev \\
-DCMAKE_BUILD_TYPE=Release \\ -DCMAKE_BUILD_TYPE=Release \\
-DEXTERNAL_MODULES_DIR='${external_modules_dir.join(";")}' \\ -DEXTERNAL_MODULES_DIR='${external_modules_dir.join(";")}' \\
-DDESKTOP_FONTS='${external_fonts.join(";")}' \\
-DJS_BUNDLE_PATH='${workspace}/${packageFolder}/StatusIm.jsbundle' \\ -DJS_BUNDLE_PATH='${workspace}/${packageFolder}/StatusIm.jsbundle' \\
-DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1' -DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1'
""" """
@ -154,6 +161,7 @@ def compileMacOS() {
cmake -Wno-dev \\ cmake -Wno-dev \\
-DCMAKE_BUILD_TYPE=Release \\ -DCMAKE_BUILD_TYPE=Release \\
-DEXTERNAL_MODULES_DIR='${external_modules_dir.join(";")}' \\ -DEXTERNAL_MODULES_DIR='${external_modules_dir.join(";")}' \\
-DDESKTOP_FONTS='${external_fonts.join(";")}' \\
-DJS_BUNDLE_PATH='${workspace}/${packageFolder}/StatusIm.jsbundle' \\ -DJS_BUNDLE_PATH='${workspace}/${packageFolder}/StatusIm.jsbundle' \\
-DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1' -DCMAKE_CXX_FLAGS:='-DBUILD_FOR_BUNDLE=1'
""" """

31
ci/ios.groovy Normal file
View File

@ -0,0 +1,31 @@
def compile(type = 'nightly') {
def target = (type == 'release' ? 'adhoc' : 'nightly')
withCredentials([
string(credentialsId: 'SLACK_URL', variable: 'SLACK_URL'),
string(credentialsId: "slave-pass-${env.NODE_NAME}", variable: 'KEYCHAIN_PASSWORD'),
string(credentialsId: 'FASTLANE_PASSWORD', variable: 'FASTLANE_PASSWORD'),
string(credentialsId: 'APPLE_ID', variable: 'APPLE_ID'),
string(credentialsId: 'fastlane-match-password', variable:'MATCH_PASSWORD')
]) {
sh "plutil -replace CFBundleShortVersionString -string ${common.version()} ios/StatusIm/Info.plist"
sh "plutil -replace CFBundleVersion -string ${common.tagBuild()} ios/StatusIm/Info.plist"
sh "fastlane ios ${target}"
}
def pkg = "StatusIm-${GIT_COMMIT.take(6)}.ipa"
sh "cp status-adhoc/StatusIm.ipa ${pkg}"
return pkg
}
def uploadToDiawi() {
withCredentials([
string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'),
string(credentialsId: 'GIT_HUB_TOKEN', variable: 'GITHUB_TOKEN'),
string(credentialsId: 'SLACK_JENKINS_WEBHOOK', variable: 'SLACK_URL')
]) {
sh 'fastlane ios upload_diawi'
}
diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out"
return diawiUrl
}
return this

View File

@ -1,22 +1,19 @@
common = load 'ci/common.groovy' common = load 'ci/common.groovy'
ios = load 'ci/ios.groovy'
android = load 'ci/android.groovy'
def uploadArtifact() { def prep(type = 'debug') {
def artifact_dir = pwd() + '/android/app/build/outputs/apk/release/' /* select type of build */
println (artifact_dir + 'app-release.apk') switch (type) {
def artifact = (artifact_dir + 'app-release.apk') case 'debug':
def server = Artifactory.server('artifacts') sh 'cp .env.nightly .env'; break
def filename = "im.status.ethereum-${GIT_COMMIT.take(6)}-n-fl.apk" case 'release':
def newArtifact = (artifact_dir + filename) sh 'cp .env.prod .env'; break
sh "cp ${artifact} ${newArtifact}" case 'e2e':
def uploadSpec = '{ "files": [ { "pattern": "*apk/release/' + filename + '", "target": "nightlies-local" }]}' sh 'cp .env.e2e .env'; break
def buildInfo = server.upload(uploadSpec)
return 'http://artifacts.status.im:8081/artifactory/nightlies-local/' + filename
} }
def prepDeps() {
sh 'rm -rf node_modules'
sh 'cp .env.nightly .env'
common.installJSDeps('mobile') common.installJSDeps('mobile')
/* install Maven dependencies */
sh 'mvn -f modules/react-native-status/ios/RCTStatus dependency:unpack' sh 'mvn -f modules/react-native-status/ios/RCTStatus dependency:unpack'
/* generate ios/StatusIm.xcworkspace */ /* generate ios/StatusIm.xcworkspace */
dir('ios') { dir('ios') {
@ -32,70 +29,4 @@ def leinBuild() {
sh 'lein prod-build' sh 'lein prod-build'
} }
def compileAndroid(e2e = false) {
if (e2e) {
env.ENVFILE=".env.e2e"
}
dir('android') {
sh './gradlew react-native-android:installArchives'
sh './gradlew assembleRelease'
}
def pkg = "StatusIm-${GIT_COMMIT.take(6)}.apk"
sh "cp android/app/build/outputs/apk/release/app-release.apk ${pkg}"
return pkg
}
def bundleAndroid() {
withCredentials([
string(credentialsId: "SUPPLY_JSON_KEY_DATA", variable: 'GOOGLE_PLAY_JSON_KEY'),
string(credentialsId: "SLACK_URL", variable: 'SLACK_URL')
]) {
sh 'bundle exec fastlane android nightly'
}
}
def uploadSauceLabs() {
env.SAUCE_LABS_APK = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
withCredentials([
string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'),
string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'),
string(credentialsId: 'GIT_HUB_TOKEN', variable: 'GITHUB_TOKEN'),
string(credentialsId: 'SLACK_JENKINS_WEBHOOK', variable: 'SLACK_URL')
]) {
sh 'fastlane android saucelabs'
}
return env.SAUCE_LABS_APK
}
def compileiOS() {
version = readFile("${env.WORKSPACE}/VERSION").trim()
build_no = common.tagBuild()
withCredentials([
string(credentialsId: 'SLACK_URL', variable: 'SLACK_URL'),
string(credentialsId: "slave-pass-${env.NODE_NAME}", variable: 'KEYCHAIN_PASSWORD'),
string(credentialsId: 'FASTLANE_PASSWORD', variable: 'FASTLANE_PASSWORD'),
string(credentialsId: 'APPLE_ID', variable: 'APPLE_ID'),
string(credentialsId: 'fastlane-match-password', variable:'MATCH_PASSWORD')
]) {
sh "plutil -replace CFBundleShortVersionString -string ${version} ios/StatusIm/Info.plist"
sh "plutil -replace CFBundleVersion -string ${build_no} ios/StatusIm/Info.plist"
sh 'fastlane ios nightly'
}
def pkg = "StatusIm-${GIT_COMMIT.take(6)}.ipa"
sh "cp status-adhoc/StatusIm.ipa ${pkg}"
return pkg
}
def uploadiOS() {
withCredentials([
string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'),
string(credentialsId: 'GIT_HUB_TOKEN', variable: 'GITHUB_TOKEN'),
string(credentialsId: 'SLACK_JENKINS_WEBHOOK', variable: 'SLACK_URL')
]) {
sh 'fastlane android upload_diawi'
}
diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out"
return diawiUrl
}
return this return this

View File

@ -134,8 +134,7 @@ platform :ios do
end end
desc "`fastlane ios nightly` - makes a new nightly in TestFlight" desc "`fastlane ios nightly` - makes a new nightly in TestFlight"
desc "This lane builds a new nightly and uploads it to TestFlight" desc "This lane builds a new nightly and leaves an .ipa that is ad-hoc signed (can be uploaded to diawi)"
desc "It also leaves an .ipa that is ad-hoc signed (can be uploaded to diawi)"
lane :nightly do lane :nightly do
unlock_keychain_if_needed unlock_keychain_if_needed

View File

@ -1,4 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env sh
set -e
# #
# This script manages app build numbers. # This script manages app build numbers.
# It returns the next build number to be used. # It returns the next build number to be used.
@ -18,7 +21,7 @@
# #
getNumber () { getNumber () {
echo "$1" | sed 's/[^0-9]*//g' echo "$BUILD" | sed 's/[^0-9]*//g'
} }
REGEX='^build-[0-9]\+$' REGEX='^build-[0-9]\+$'
@ -26,34 +29,16 @@ REGEX='^build-[0-9]\+$'
# make sure we have all the tags # make sure we have all the tags
git fetch --tags --quiet >/dev/null || >&2 echo "Could not fetch tags from remote" git fetch --tags --quiet >/dev/null || >&2 echo "Could not fetch tags from remote"
# check if current commit has a build tag # even if the current commit has a tag already, it is normal that the same commit
# since we are building in separate jobs we have to check for a tag # is built multiple times (with different build configurations, for instance),
BUILD_TAG=$(git tag --points-at HEAD | grep -e "$REGEX") # so we increment the build number every time.
# chech for multiple lines
if [ 1 -lt $(echo "$BUILD_TAG" | grep -c -) ]; then
echo "Commit marked with more than one build tag!" >&2
echo "$BUILD_TAG" >&2
exit 1
fi
# use already existing build number if applicable
if [ -n "$BUILD_TAG" ]; then
echo "Current commit already tagged: $BUILD_TAG" >&2
getNumber $BUILD_TAG
exit 0
fi
# if no tag was found and --increment was not given stop
if [ "$1" != "--increment" ]; then
exit 0
fi
# find the last used build number # find the last used build number
BUILD=$(git tag -l --sort=-v:refname | grep -e "$REGEX" | head -n 1) BUILD=$(git tag -l --sort=-v:refname | grep -e "$REGEX" | head -n 1)
# extract the number # extract the number
BUILD_NO=$(getNumber "$BUILD") BUILD_NO=$(getNumber "$BUILD")
if [ "$1" = "--increment" ]; then
# These need to be provided by Jenkins # These need to be provided by Jenkins
if [ -z "${GIT_USER}" ] || [ -z "${GIT_PASS}" ]; then if [ -z "${GIT_USER}" ] || [ -z "${GIT_PASS}" ]; then
echo "Git credentials not specified! (GIT_USER, GIT_PASS)" >&2 echo "Git credentials not specified! (GIT_USER, GIT_PASS)" >&2
@ -65,6 +50,7 @@ BUILD_NO="$((BUILD_NO+1))"
echo "Tagging HEAD: build-$BUILD_NO" >&2 echo "Tagging HEAD: build-$BUILD_NO" >&2
git tag "build-$BUILD_NO" HEAD git tag "build-$BUILD_NO" HEAD
git push --tags https://${GIT_USER}:${GIT_PASS}@github.com/status-im/status-react git push --tags https://${GIT_USER}:${GIT_PASS}@github.com/status-im/status-react
fi
# finally print build number # finally print build number
echo "$BUILD_NO" echo "$BUILD_NO"