From 4e567cf7828cc4e2a10eeb1108e4ec2d0cdc3b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Soko=C5=82owski?= Date: Fri, 6 Mar 2020 16:43:04 +0100 Subject: [PATCH] use status-react-jenkins as CI library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extracts all the ci/*.groovy scripts into a separate private repo located at: https://github.com/status-im/status-react-jenkins The main reasons for a separate repo are: * Hiding the internal details of our CI setup * Hiding names of Jenkins credentials available in CI jobs * Lowering attack surface for malicious external contributors * Increasing focus on PRs related to CI setup You can read more about how Jenkins pipeline shared libraries work here: https://jenkins.io/doc/book/pipeline/shared-libraries/ In simple terms I've added the repo to the main Jenkins configuration in "Global Pipeline Libraries" section and load it using: library 'status-react-jenkins@master' Which makes globally available all of the libraries defined in the `vars` directory of that repo. This also eliminates the need for statements like `android = load 'ci/android.groovy'`. Signed-off-by: Jakub SokoĊ‚owski --- ci/Jenkinsfile.android | 25 ++--- ci/Jenkinsfile.combined | 42 +++---- ci/Jenkinsfile.fastlane.clean | 3 +- ci/Jenkinsfile.ios | 23 ++-- ci/Jenkinsfile.linux | 21 ++-- ci/Jenkinsfile.macos | 21 ++-- ci/Jenkinsfile.nix-cache | 7 +- ci/Jenkinsfile.windows | 21 ++-- ci/README.md | 16 +++ ci/android.groovy | 178 ----------------------------- ci/common.groovy | 106 ------------------ ci/desktop.groovy | 109 ------------------ ci/ghcmgr.groovy | 62 ---------- ci/github.groovy | 205 ---------------------------------- ci/ios.groovy | 102 ----------------- ci/jenkins.groovy | 92 --------------- ci/nix.groovy | 170 ---------------------------- ci/utils.groovy | 151 ------------------------- scripts/generate-keystore.sh | 2 +- 19 files changed, 95 insertions(+), 1261 deletions(-) create mode 100644 ci/README.md delete mode 100644 ci/android.groovy delete mode 100644 ci/common.groovy delete mode 100644 ci/desktop.groovy delete mode 100644 ci/ghcmgr.groovy delete mode 100644 ci/github.groovy delete mode 100644 ci/ios.groovy delete mode 100644 ci/jenkins.groovy delete mode 100644 ci/nix.groovy delete mode 100644 ci/utils.groovy diff --git a/ci/Jenkinsfile.android b/ci/Jenkinsfile.android index bc4b4e2859..39ff57ba57 100644 --- a/ci/Jenkinsfile.android +++ b/ci/Jenkinsfile.android @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'linux' } @@ -40,14 +42,11 @@ pipeline { stage('Prep') { steps { script { - /* Necessary to load methods */ - android = load 'ci/android.groovy' - cmn = load 'ci/common.groovy' - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() print "Running ${btype} build!" - cmn.ci.abortPreviousRunningBuilds() + jenkins.abortPreviousRunningBuilds() /* Cleanup and Prep */ - cmn.prep(btype) + commonPrep(btype) } } } @@ -56,7 +55,7 @@ pipeline { /* Build implicit dependencies if needed (we run `lein deps :tree` but it's not really required, for this purpose) Implicit dependencies include building a patched node_modules, fetching maven dependencies, and anything else required. We do this before the parallel steps so we have a known starting situation. */ - script { cmn.nix.shell('lein deps :tree', attr: 'shells.lein') } + script { nix.shell('lein deps :tree', attr: 'shells.lein') } } } stage('Parallel Assemble') { @@ -64,12 +63,12 @@ pipeline { stage('Checks') { stages { stage('Lint') { steps { - script { cmn.nix.shell('lein cljfmt check', attr: 'shells.lein') } + script { nix.shell('lein cljfmt check', attr: 'shells.lein') } } } stage('Tests') { steps { - script { cmn.nix.shell('lein test-cljs', attr: 'shells.lein') } + script { nix.shell('lein test-cljs', attr: 'shells.lein') } } } } } @@ -77,7 +76,7 @@ pipeline { stage('JSBundle') { steps { script { - cmn.nix.shell('make jsbundle-android', pure: false) + nix.shell('make jsbundle-android', pure: false) } } } @@ -99,7 +98,7 @@ pipeline { stage('Upload') { steps { script { - def urls = apks.collect { cmn.uploadArtifact(it) } + def urls = apks.collect { s3.uploadArtifact(it) } /* return only the universal APK */ if (urls.size() > 1) { env.PKG_URL = urls.find { it.contains('universal') } @@ -125,8 +124,8 @@ pipeline { } } post { - success { script { load('ci/github.groovy').notifyPR(true) } } - failure { script { load('ci/github.groovy').notifyPR(false) } } + success { script { github.notifyPR(true) } } + failure { script { github.notifyPR(false) } } always { sh 'make _fix-node-perms' } } } diff --git a/ci/Jenkinsfile.combined b/ci/Jenkinsfile.combined index 0deb1e87e2..c6bb49b0a7 100644 --- a/ci/Jenkinsfile.combined +++ b/ci/Jenkinsfile.combined @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'linux' } @@ -18,36 +20,33 @@ pipeline { stage('Prep') { steps { script { println "Current JOB: ${env.JOB_NAME}" - /* load common lib */ - cmn = load('ci/common.groovy') - gh = load('ci/github.groovy') /* just for a shorter access */ - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() } } } stage('Build') { parallel { stage('iOS') { steps { script { - ios = cmn.ci.Build('status-react/combined/mobile-ios') + ios = jenkins.Build('status-react/combined/mobile-ios') } } } stage('Android') { steps { script { - apk = cmn.ci.Build('status-react/combined/mobile-android') + apk = jenkins.Build('status-react/combined/mobile-android') } } } stage('Android e2e') { steps { script { - apke2e = cmn.ci.Build('status-react/combined/mobile-android-e2e') + apke2e = jenkins.Build('status-react/combined/mobile-android-e2e') } } } } } stage('Archive') { steps { script { sh('rm -f pkg/*') - cmn.ci.copyArts(ios) - //cmn.ci.copyArts(iose2e) - cmn.ci.copyArts(apk) - cmn.ci.copyArts(apke2e) + jenkins.copyArts(ios) + //jenkins.copyArts(iose2e) + jenkins.copyArts(apk) + jenkins.copyArts(apke2e) dir('pkg') { /* generate sha256 checksums for upload */ - sh "sha256sum * | tee ${cmn.utils.pkgFilename(btype, 'sha256')}" + sh "sha256sum * | tee ${utils.pkgFilename(btype, 'sha256')}" archiveArtifacts('*') } } } @@ -57,18 +56,18 @@ pipeline { /* object for easier URLs handling */ urls = [ /* mobile */ - Apk: cmn.pkgUrl(apk), Apke2e: cmn.pkgUrl(apke2e), - iOS: cmn.pkgUrl(ios), /*iOSe2e: cmn.pkgUrl(iose2e),*/ - Diawi: cmn.utils.getEnv(ios, 'DIAWI_URL'), + Apk: utils.pkgUrl(apk), Apke2e: utils.pkgUrl(apke2e), + iOS: utils.pkgUrl(ios), /*iOSe2e: utils.pkgUrl(iose2e),*/ + Diawi: utils.utils.getEnv(ios, 'DIAWI_URL'), /* upload the sha256 checksums file too */ - SHA: cmn.uploadArtifact(cmn.utils.pkgFind('sha256')), + SHA: s3.uploadArtifact(utils.pkgFind('sha256')), ] /* add URLs to the build description */ - cmn.ci.setBuildDesc(urls) + jenkins.setBuildDesc(urls) /* Create JSON file with newest build URLs */ switch (btype) { /* legacy naming, should have named it nightly.json */ - case 'nightly': cmn.updateBucketJSON(urls, 'latest.json'); break + case 'nightly': s3.updateBucketJSON(urls, 'latest.json'); break } } } } @@ -83,10 +82,11 @@ pipeline { stage('Run e2e') { when { expression { btype == 'nightly' } } steps { script { - e2eApk = cmn.utils.getEnv(apke2e, 'SAUCE_URL') + e2eApk = utils.getEnv(apke2e, 'SAUCE_URL') build( - job: 'end-to-end-tests/status-app-nightly', wait: false, - parameters: [string(name: 'APK_NAME', value: e2eApk)] + job: 'end-to-end-tests/status-app-nightly', + parameters: [string(name: 'APK_NAME', value: e2eApk)], + wait: false ) } } } diff --git a/ci/Jenkinsfile.fastlane.clean b/ci/Jenkinsfile.fastlane.clean index a8cf277f68..6435dec75e 100644 --- a/ci/Jenkinsfile.fastlane.clean +++ b/ci/Jenkinsfile.fastlane.clean @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'macos' } @@ -26,7 +28,6 @@ pipeline { stages { stage('Prep') { steps { script { - nix = load('ci/nix.groovy') nix.shell( 'bundle install --gemfile=fastlane/Gemfile', attr: 'shells.fastlane', diff --git a/ci/Jenkinsfile.ios b/ci/Jenkinsfile.ios index 4eb99098fe..3c8211138c 100644 --- a/ci/Jenkinsfile.ios +++ b/ci/Jenkinsfile.ios @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'macos-xcode-11.3.1' } @@ -39,14 +41,11 @@ pipeline { stage('Prep') { steps { script { - /* Necessary to load methods */ - ios = load 'ci/ios.groovy' - cmn = load 'ci/common.groovy' - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() print "Running ${btype} build!" - cmn.ci.abortPreviousRunningBuilds() + jenkins.abortPreviousRunningBuilds() /* Cleanup and Prep */ - cmn.prep(btype) + commonPrep(btype) } } } @@ -55,19 +54,19 @@ pipeline { stage('Checks') { stages { stage('Lint') { steps { - script { cmn.nix.shell('lein cljfmt check', attr: 'shells.lein') } + script { nix.shell('lein cljfmt check', attr: 'shells.lein') } } } stage('Tests') { steps { - script { cmn.nix.shell('lein test-cljs', attr: 'shells.lein') } + script { nix.shell('lein test-cljs', attr: 'shells.lein') } } } } } stage('Build') { stages { stage('JSBundle') { steps { - script { cmn.nix.shell('make jsbundle-ios') } + script { nix.shell('make jsbundle-ios') } } } stage('Bundle') { @@ -88,7 +87,7 @@ pipeline { stage('Upload') { steps { script { - env.PKG_URL = cmn.uploadArtifact(api) + env.PKG_URL = s3.uploadArtifact(api) /* e2e builds get tested in SauceLabs */ if (btype == 'e2e') { env.SAUCE_URL = ios.uploadToSauceLabs() @@ -108,8 +107,8 @@ pipeline { } } post { - success { script { load('ci/github.groovy').notifyPR(true) } } - failure { script { load('ci/github.groovy').notifyPR(false) } } + success { script { github.notifyPR(true) } } + failure { script { github.notifyPR(false) } } always { sh 'make _fix-node-perms' } } } diff --git a/ci/Jenkinsfile.linux b/ci/Jenkinsfile.linux index 4854a7853b..39e41c39bf 100644 --- a/ci/Jenkinsfile.linux +++ b/ci/Jenkinsfile.linux @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'linux' } @@ -42,14 +44,11 @@ pipeline { stage('Prep') { steps { script { - /* Necessary to load methods */ - desktop = load 'ci/desktop.groovy' - cmn = load 'ci/common.groovy' - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() print "Running ${btype} build!" - cmn.ci.abortPreviousRunningBuilds() + jenkins.abortPreviousRunningBuilds() /* Cleanup and Prep */ - cmn.prep(btype) + commonPrep(btype) } } } @@ -58,12 +57,12 @@ pipeline { stage('Checks') { stages { stage('Lint') { steps { - script { cmn.nix.shell('lein cljfmt check', attr: 'shells.lein') } + script { nix.shell('lein cljfmt check', attr: 'shells.lein') } } } stage('Tests') { steps { - script { cmn.nix.shell('lein test-cljs', attr: 'shells.lein') } + script { nix.shell('lein test-cljs', attr: 'shells.lein') } } } } } @@ -95,7 +94,7 @@ pipeline { } stage('Upload') { steps { - script { env.PKG_URL = cmn.uploadArtifact(app) } + script { env.PKG_URL = s3.uploadArtifact(app) } } } } @@ -108,7 +107,7 @@ pipeline { } } post { - success { script { load('ci/github.groovy').notifyPR(true) } } - failure { script { load('ci/github.groovy').notifyPR(false) } } + success { script { github.notifyPR(true) } } + failure { script { github.notifyPR(false) } } } } diff --git a/ci/Jenkinsfile.macos b/ci/Jenkinsfile.macos index c52921bafd..f0461d9d9b 100644 --- a/ci/Jenkinsfile.macos +++ b/ci/Jenkinsfile.macos @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'macos-xcode-11.3.1' } @@ -40,14 +42,11 @@ pipeline { stage('Prep') { steps { script { - /* Necessary to load methods */ - desktop = load 'ci/desktop.groovy' - cmn = load 'ci/common.groovy' - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() print "Running ${btype} build!" - cmn.ci.abortPreviousRunningBuilds() + jenkins.abortPreviousRunningBuilds() /* Cleanup and Prep */ - cmn.prep(btype) + commonPrep(btype) } } } @@ -56,12 +55,12 @@ pipeline { stage('Checks') { stages { stage('Lint') { steps { - script { cmn.nix.shell('lein cljfmt check', attr: 'shells.lein') } + script { nix.shell('lein cljfmt check', attr: 'shells.lein') } } } stage('Tests') { steps { - script { cmn.nix.shell('lein test-cljs', attr: 'shells.lein') } + script { nix.shell('lein test-cljs', attr: 'shells.lein') } } } } } @@ -93,7 +92,7 @@ pipeline { } stage('Upload') { steps { - script { env.PKG_URL = cmn.uploadArtifact(dmg) } + script { env.PKG_URL = s3.uploadArtifact(dmg) } } } } @@ -106,7 +105,7 @@ pipeline { } } post { - success { script { load('ci/github.groovy').notifyPR(true) } } - failure { script { load('ci/github.groovy').notifyPR(false) } } + success { script { github.notifyPR(true) } } + failure { script { github.notifyPR(false) } } } } diff --git a/ci/Jenkinsfile.nix-cache b/ci/Jenkinsfile.nix-cache index c92af3c4b1..4e496a792f 100644 --- a/ci/Jenkinsfile.nix-cache +++ b/ci/Jenkinsfile.nix-cache @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label params.AGENT_LABEL } @@ -25,11 +27,6 @@ pipeline { } stages { - stage('Prep') { - steps { script { - nix = load('ci/nix.groovy') - } } - } stage('Setup') { steps { script { nix.shell('nix-env -i openssh', pure: false) diff --git a/ci/Jenkinsfile.windows b/ci/Jenkinsfile.windows index 3db1a5403b..cdc84b94b2 100644 --- a/ci/Jenkinsfile.windows +++ b/ci/Jenkinsfile.windows @@ -1,3 +1,5 @@ +library 'status-react-jenkins@master' + pipeline { agent { label 'linux' } @@ -45,14 +47,11 @@ pipeline { stage('Prep') { steps { script { - /* Necessary to load methods */ - desktop = load 'ci/desktop.groovy' - cmn = load 'ci/common.groovy' - btype = cmn.utils.getBuildType() + btype = utils.getBuildType() print "Running ${btype} build!" - cmn.ci.abortPreviousRunningBuilds() + jenkins.abortPreviousRunningBuilds() /* Cleanup and Prep */ - cmn.prep(btype) + commonPrep(btype) } } } @@ -61,12 +60,12 @@ pipeline { stage('Checks') { stages { stage('Lint') { steps { - script { cmn.nix.shell('lein cljfmt check', attr: 'shells.lein') } + script { nix.shell('lein cljfmt check', attr: 'shells.lein') } } } stage('Tests') { steps { - script { cmn.nix.shell('lein test-cljs', attr: 'shells.lein') } + script { nix.shell('lein test-cljs', attr: 'shells.lein') } } } } } @@ -98,7 +97,7 @@ pipeline { } stage('Upload') { steps { - script { env.PKG_URL = cmn.uploadArtifact(app) } + script { env.PKG_URL = s3.uploadArtifact(app) } } } } @@ -111,7 +110,7 @@ pipeline { } } post { - success { script { load('ci/github.groovy').notifyPR(true) } } - failure { script { load('ci/github.groovy').notifyPR(false) } } + success { script { github.notifyPR(true) } } + failure { script { github.notifyPR(false) } } } } diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000000..5b4b63812a --- /dev/null +++ b/ci/README.md @@ -0,0 +1,16 @@ +# Description + +This folder contains files defininf [Jenkins pipelines](https://jenkins.io/doc/book/pipeline/) that run on https://ci.status.im/. + +# Libraries + +All `Jenkinsfile`s contain the following line: +```groovy +library 'status-react-jenkins@master' +``` + +Which loads the used methods - like `nix.shell()` - from a separate private repo: + +https://github.com/status-im/status-react-jenkins + +This is done to improve security of our CI setup. diff --git a/ci/android.groovy b/ci/android.groovy deleted file mode 100644 index b13640e8b2..0000000000 --- a/ci/android.groovy +++ /dev/null @@ -1,178 +0,0 @@ -nix = load 'ci/nix.groovy' -utils = load 'ci/utils.groovy' - -def bundle() { - /* we use the method because parameter build type does not take e2e into account */ - def btype = utils.getBuildType() - /* Disable Gradle Daemon https://stackoverflow.com/questions/38710327/jenkins-builds-fail-using-the-gradle-daemon */ - def gradleOpt = "-PbuildUrl='${currentBuild.absoluteUrl}' --console plain " - /* Can't take more digits than unsigned int */ - def buildNumber = utils.readBuildNumber().substring(0, 10) - /* we don't need x86 for any builds except e2e */ - env.ANDROID_ABI_INCLUDE="armeabi-v7a;arm64-v8a" - env.ANDROID_ABI_SPLIT="false" - - /* some builds tyes require different architectures */ - switch (btype) { - case 'e2e': - env.ANDROID_ABI_INCLUDE="x86" /* e2e builds are used with simulators */ - break - case 'release': - env.ANDROID_ABI_SPLIT="true" - gradleOpt += "-PreleaseVersion='${utils.getVersion()}'" - break - } - - /* credentials necessary to open the keystore and sign the APK */ - withCredentials([ - file( - credentialsId: 'status-im.keystore', - variable: 'KEYSTORE_PATH' - ), - string( - credentialsId: 'android-keystore-pass', - variable: 'KEYSTORE_PASSWORD' - ), - usernamePassword( - credentialsId: 'android-keystore-key-pass', - usernameVariable: 'KEYSTORE_ALIAS', - passwordVariable: 'KEYSTORE_KEY_PASSWORD' - ) - ]) { - /* Nix target which produces the final APKs */ - nix.build( - attr: 'targets.mobile.android.release', - conf: [ - 'status-im.ci': '1', - 'status-im.build-type': btype, - 'status-im.status-react.gradle-opts': gradleOpt, - 'status-im.status-react.build-number': buildNumber, - ], - safeEnv: [ - 'KEYSTORE_ALIAS', - 'KEYSTORE_PASSWORD', - 'KEYSTORE_KEY_PASSWORD', - ], - keepEnv: [ - 'ANDROID_ABI_SPLIT', - 'ANDROID_ABI_INCLUDE', - 'KEYSTORE_PATH', - ], - sandboxPaths: [ - env.KEYSTORE_PATH, - ], - link: false - ) - } - /* necessary for Fastlane */ - def apks = renameAPKs() - /* for use with Fastlane */ - env.APK_PATHS = apks.join(";") - return apks -} - -def extractArchFromAPK(name) { - def pattern = /app-(.+)-[^-]+.apk/ - /* extract architecture from filename */ - def matches = (name =~ pattern) - if (matches.size() > 0) { - return matches[0][1] - } - if (utils.isE2EBuild()) { - return 'x86' - } - /* non-release builds make universal APKs */ - return 'universal' -} - -/** - * We need more informative filenames for all builds. - * For more details on the format see utils.pkgFilename(). - **/ -def renameAPKs() { - /* find all APK files */ - def apkGlob = 'result/*.apk' - def found = findFiles(glob: apkGlob) - if (found.size() == 0) { - throw "APKs not found via glob: ${apkGlob}" - } - def renamed = [] - /* rename each for upload & archiving */ - for (apk in found) { - def arch = extractArchFromAPK(apk) - def pkg = utils.pkgFilename(env.BUILD_TYPE, 'apk', arch) - def newApk = "result/${pkg}" - renamed += newApk - sh "cp ${apk.path} ${newApk}" - } - return renamed -} - -def uploadToPlayStore(type = 'nightly') { - withCredentials([ - string(credentialsId: "SUPPLY_JSON_KEY_DATA", variable: 'GOOGLE_PLAY_JSON_KEY'), - ]) { - nix.shell( - "fastlane android ${type}", - keepEnv: ['FASTLANE_DISABLE_COLORS', 'APK_PATHS', 'GOOGLE_PLAY_JSON_KEY'], - attr: 'shells.fastlane', - pure: false - ) - } -} - -def uploadToSauceLabs() { - def changeId = utils.changeId() - if (changeId != null) { - env.SAUCE_LABS_NAME = "${changeId}.apk" - } else { - def pkg = utils.pkgFilename(env.BUILD_TYPE, 'apk', 'x86') - env.SAUCE_LABS_NAME = "${pkg}" - } - withCredentials([ - usernamePassword( - credentialsId: 'sauce-labs-api', - usernameVariable: 'SAUCE_USERNAME', - passwordVariable: 'SAUCE_ACCESS_KEY' - ), - ]) { - nix.shell( - 'fastlane android saucelabs', - keepEnv: [ - 'FASTLANE_DISABLE_COLORS', 'APK_PATHS', - 'SAUCE_ACCESS_KEY', 'SAUCE_USERNAME', 'SAUCE_LABS_NAME' - ], - attr: 'shells.fastlane', - pure: false - ) - } - return env.SAUCE_LABS_NAME -} - -def uploadToDiawi() { - withCredentials([ - string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'), - ]) { - nix.shell( - 'fastlane android upload_diawi', - keepEnv: ['FASTLANE_DISABLE_COLORS', 'APK_PATHS', 'DIAWI_TOKEN'], - attr: 'shells.fastlane', - pure: false - ) - } - diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out" - return diawiUrl -} - -def coverage() { - withCredentials([ - string(credentialsId: 'coveralls-status-react-token', variable: 'COVERALLS_REPO_TOKEN'), - ]) { - nix.shell( - 'make coverage', - keepEnv: ['COVERALLS_REPO_TOKEN', 'COVERALLS_SERVICE_NAME', 'COVERALLS_SERVICE_JOB_ID'] - ) - } -} - -return this diff --git a/ci/common.groovy b/ci/common.groovy deleted file mode 100644 index b89009cd6c..0000000000 --- a/ci/common.groovy +++ /dev/null @@ -1,106 +0,0 @@ -import groovy.json.JsonBuilder - -/* Libraries -----------------------------------------------------------------*/ - -ci = load 'ci/jenkins.groovy' -nix = load 'ci/nix.groovy' -utils = load 'ci/utils.groovy' - -/* Small Helpers -------------------------------------------------------------*/ - -def pkgUrl(build) { - return utils.getEnv(build, 'PKG_URL') -} - -def updateBucketJSON(urls, fileName) { - /* latest.json has slightly different key names */ - def content = [ - DIAWI: urls.Diawi, - APK: urls.Apk, IOS: urls.iOS, - APP: urls.App, MAC: urls.Mac, - WIN: urls.Win, SHA: urls.SHA - ] - def filePath = "${pwd()}/pkg/${fileName}" - /* it might not exist */ - sh "mkdir -p ${pwd()}/pkg" - def contentJson = new JsonBuilder(content).toPrettyString() - println "${filePath}:\n${contentJson}" - writeFile(file: filePath, text: contentJson) - return uploadArtifact(filePath) -} - -def prep(type = 'nightly') { - /* build/downloads all nix deps in advance */ - nix.prepEnv() - /* rebase unless this is a release build */ - utils.doGitRebase() - /* ensure that we start from a known state */ - sh 'make clean' - /* Disable git hooks in CI, it's not useful, takes time and creates weird errors at times - (e.g. bin/sh: 2: /etc/ssl/certs/ca-certificates.crt: Permission denied) */ - sh 'make disable-githooks' - - /* pick right .env and update from params */ - utils.updateEnv(type) - - if (['android', 'ios'].contains(env.TARGET)) { - /* Run at start to void mismatched numbers */ - utils.genBuildNumber() - } - - nix.shell('watchman watch-del-all', attr: 'shells.watchman') - - if (env.TARGET == 'ios') { - /* install ruby dependencies */ - nix.shell( - 'bundle install --gemfile=fastlane/Gemfile --quiet', - attr: 'shells.fastlane', - sandbox: false - ) - } - - if (['macos', 'linux', 'windows'].contains(env.TARGET)) { - /* node deps, pods, and status-go download */ - nix.shell('scripts/prepare-for-desktop-platform.sh', pure: false) - } - /* run script in the nix shell so that node_modules gets instantiated before attempting the copies */ - nix.shell( - 'scripts/copy-translations.sh chmod', - attr: "shells.${env.TARGET}", - sandbox: false - ) -} - -def uploadArtifact(path) { - /* defaults for upload */ - def domain = 'ams3.digitaloceanspaces.com' - def bucket = 'status-im' - /* There's so many PR builds we need a separate bucket */ - if (utils.isPRBuild()) { - bucket = 'status-im-prs' - } - /* WARNING: s3cmd can't guess APK MIME content-type */ - def customOpts = '' - if (path.endsWith('apk')) { - customOpts += "--mime-type='application/vnd.android.package-archive'" - } - /* We also need credentials for the upload */ - withCredentials([usernamePassword( - credentialsId: 'digital-ocean-access-keys', - usernameVariable: 'DO_ACCESS_KEY', - passwordVariable: 'DO_SECRET_KEY' - )]) { - sh(""" - s3cmd ${customOpts} \\ - --acl-public \\ - --host="${domain}" \\ - --host-bucket="%(bucket)s.${domain}" \\ - --access_key=${DO_ACCESS_KEY} \\ - --secret_key=${DO_SECRET_KEY} \\ - put ${path} s3://${bucket}/ - """) - } - return "https://${bucket}.${domain}/${utils.getFilename(path)}" -} - -return this diff --git a/ci/desktop.groovy b/ci/desktop.groovy deleted file mode 100644 index 70b96a6c20..0000000000 --- a/ci/desktop.groovy +++ /dev/null @@ -1,109 +0,0 @@ -nix = load 'ci/nix.groovy' -utils = load 'ci/utils.groovy' - -packageFolder = './StatusImPackage' - -def buildJSBundle() { - nix.shell( - ''' - make jsbundle-desktop && \ - ./scripts/build-desktop.sh buildJSBundle - ''', - keepEnv: ['VERBOSE_LEVEL'] - ) -} - -def uploadArtifact(filename) { - def domain = 'ams3.digitaloceanspaces.com' - def bucket = 'status-im-desktop' - withCredentials([usernamePassword( - credentialsId: 'digital-ocean-access-keys', - usernameVariable: 'DO_ACCESS_KEY', - passwordVariable: 'DO_SECRET_KEY' - )]) { - sh """ - s3cmd \\ - --acl-public \\ - --host='${domain}' \\ - --host-bucket='%(bucket)s.${domain}' \\ - --access_key=${DO_ACCESS_KEY} \\ - --secret_key=${DO_SECRET_KEY} \\ - put ${filename} s3://${bucket}/ - """ - - } - def url = "https://${bucket}.${domain}/${filename}" - return url -} - -/* MAIN --------------------------------------------------*/ - -def compile() { - /* disable logs for desktop builds when releasing */ - if (params.BUILD_TYPE == 'release') { - env.STATUS_NO_LOGGING = 1 - } - /* since QT is in a custom path we need to add it to PATH */ - if (env.QT_PATH) { - env.PATH = "${env.QT_PATH}:${env.PATH}" - } - nix.shell( - './scripts/build-desktop.sh compile', - keepEnv: ['VERBOSE_LEVEL'] - ) -} - -def bundleWindows(type = 'nightly') { - def pkg - - nix.shell( - './scripts/build-desktop.sh bundle', - keepEnv: ['VERBOSE_LEVEL'] - ) - dir(packageFolder) { - pkg = utils.pkgFilename(type, 'exe') - sh "mv ../Status-x86_64-setup.exe ${pkg}" - } - return "${packageFolder}/${pkg}".drop(2) -} - -def bundleLinux(type = 'nightly') { - def pkg - nix.shell( - './scripts/build-desktop.sh bundle', - keepEnv: ['VERBOSE_LEVEL'] - ) - dir(packageFolder) { - pkg = utils.pkgFilename(type, 'AppImage') - sh "mv ../Status-x86_64.AppImage ${pkg}" - } - return "${packageFolder}/${pkg}".drop(2) -} - -def bundleMacOS(type = 'nightly') { - def pkg = utils.pkgFilename(type, 'dmg') - nix.shell( - './scripts/build-desktop.sh bundle', - keepEnv: ['VERBOSE_LEVEL'] - ) - dir(packageFolder) { - withCredentials([ - string(credentialsId: 'desktop-gpg-outer-pass', variable: 'GPG_PASS_OUTER'), - string(credentialsId: 'desktop-gpg-inner-pass', variable: 'GPG_PASS_INNER'), - string(credentialsId: 'desktop-keychain-pass', variable: 'KEYCHAIN_PASS') - ]) { - nix.shell( - """ - ../scripts/sign-macos-pkg.sh Status.app ../deployment/macos/macos-developer-id.keychain-db.gpg && \ - ../node_modules/appdmg/bin/appdmg.js ../deployment/macos/status-dmg.json ${pkg} && \ - ../scripts/sign-macos-pkg.sh ${pkg} ../deployment/macos/macos-developer-id.keychain-db.gpg - """, - pure: false, - keepEnv: ['GPG_PASS_OUTER', 'GPG_PASS_INNER', 'KEYCHAIN_PASS'] - ) - } - } - return "${packageFolder}/${pkg}".drop(2) -} - -return this diff --git a/ci/ghcmgr.groovy b/ci/ghcmgr.groovy deleted file mode 100644 index 96f4e5c7a1..0000000000 --- a/ci/ghcmgr.groovy +++ /dev/null @@ -1,62 +0,0 @@ -import groovy.json.JsonBuilder - -utils = load 'ci/utils.groovy' - -/** - * Methods for interacting with ghcmgr API. - * For more details see: - * https://github.com/status-im/github-comment-manager - **/ - -def buildObj(success) { - def pkg_url = env.PKG_URL - /* a bare ipa file is not installable on iOS */ - if (env.TARGET == 'ios' && env.DIAWI_URL != "") { - pkg_url = env.DIAWI_URL - } - /* assemble build object valid for ghcmgr */ - return [ - id: env.BUILD_DISPLAY_NAME, - commit: GIT_COMMIT.take(8), - success: success != null ? success : true, - platform: env.TARGET + (utils.isE2EBuild() ? '-e2e' : ''), - duration: utils.buildDuration(), - url: currentBuild.absoluteUrl, - pkg_url: pkg_url, - ] -} - -def postBuild(success) { - /** - * This is our own service for avoiding comment spam. - * https://github.com/status-im/github-comment-manager - **/ - def ghcmgrurl = 'https://ghcmgr.status.im' - def body = buildObj(success) - def json = new JsonBuilder(body).toPrettyString() - def stdout = null - withCredentials([usernamePassword( - credentialsId: 'ghcmgr-auth', - usernameVariable: 'GHCMGR_USER', - passwordVariable: 'GHCMGR_PASS' - )]) { - stdout = sh( - returnStdout: true, - script: """ - curl --silent \ - -XPOST --data '${json}' \ - -u '${GHCMGR_USER}:${GHCMGR_PASS}' \ - -w '\nHTTP_CODE:%{http_code}' \ - -H "content-type: application/json" \ - '${ghcmgrurl}/builds/status-react/${utils.changeId()}' - """ - ) - } - /* We're not using --fail because it suppresses server response */ - if (!stdout.contains('HTTP_CODE:201')) { - println("STDOUT:\n${stdout}") - error("Notifying GHCMGR failed") - } -} - -return this diff --git a/ci/github.groovy b/ci/github.groovy deleted file mode 100644 index fdee24c460..0000000000 --- a/ci/github.groovy +++ /dev/null @@ -1,205 +0,0 @@ -import groovy.json.JsonBuilder - -/* I'm using utils from ghcmgr to avoid another load */ -ghcmgr = load 'ci/ghcmgr.groovy' - -/** - * Methods for interacting with GitHub API and related tools. - **/ - -/* Comments -------------------------------------------------------*/ - -def notify(message) { - def githubIssuesUrl = 'https://api.github.com/repos/status-im/status-react/issues' - def changeId = ghcmgr.utils.changeId() - if (changeId == null) { return } - def msgObj = [body: message] - def msgJson = new JsonBuilder(msgObj).toPrettyString() - withCredentials([usernamePassword( - credentialsId: 'status-im-auto', - usernameVariable: 'GH_USER', - passwordVariable: 'GH_PASS' - )]) { - sh """ - curl --silent \ - -u '${GH_USER}:${GH_PASS}' \ - --data '${msgJson}' \ - -H "Content-Type: application/json" \ - "${githubIssuesUrl}/${changeId}/comments" - """.trim() - } -} - -def notifyFull(urls) { - def msg = "#### :white_check_mark: " - msg += "[${env.JOB_NAME}${currentBuild.displayName}](${currentBuild.absoluteUrl}) " - msg += "CI BUILD SUCCESSFUL in ${ghcmgr.utils.buildDuration()} (${GIT_COMMIT.take(8)})\n" - msg += '| | | | | |\n' - msg += '|-|-|-|-|-|\n' - msg += "| [Android](${urls.Apk}) ([e2e](${urls.Apke2e})) " - msg += "| [iOS](${urls.iOS}) ([e2e](${urls.iOSe2e})) |" - if (urls.Mac != null) { - msg += " [MacOS](${urls.Mac}) | [AppImage](${urls.App}) | [Windows](${urls.Win}) |" - } else { - msg += " ~~MacOS~~ | ~~AppImage~~ | ~~Windows~~~ |" - } - notify(msg) -} - -def notifyPRFailure() { - def d = ":small_orange_diamond:" - def msg = "#### :x: " - msg += "[${env.JOB_NAME}${currentBuild.displayName}](${currentBuild.absoluteUrl}) ${d} " - msg += "${ghcmgr.utils.buildDuration()} ${d} ${GIT_COMMIT.take(8)} ${d} " - msg += "[:page_facing_up: build log](${currentBuild.absoluteUrl}/consoleText)" - //msg += "Failed in stage: ${env.STAGE_NAME}\n" - //msg += "```${currentBuild.rawBuild.getLog(5)}```" - notify(msg) -} - -def notifyPRSuccess() { - def d = ":small_blue_diamond:" - def msg = "#### :heavy_check_mark: " - def type = ghcmgr.utils.isE2EBuild() ? ' e2e' : '' - msg += "[${env.JOB_NAME}${currentBuild.displayName}](${currentBuild.absoluteUrl}) ${d} " - msg += "${ghcmgr.utils.buildDuration()} ${d} ${GIT_COMMIT.take(8)} ${d} " - msg += "[:package: ${env.TARGET}${type} package](${env.PKG_URL})" - notify(msg) -} - -/* Releases -------------------------------------------------------*/ - -def getPrevRelease() { - return sh(returnStdout: true, - script: "git for-each-ref --format '%(refname)' --sort=committerdate refs/remotes/origin/release" - ).trim().split('\\r?\\n').last() -} - -def getDiffUrl(prev, current) { - prev = prev.replaceAll(/.*origin\//, '') - return "https://github.com/status-im/status-react/compare/${prev}...${current}" -} - -def getReleaseChanges() { - def prevRelease = getPrevRelease() - def curRelease = ghcmgr.utils.branchName() - def changes = '' - try { - changes = sh(returnStdout: true, - script: """ - git log \ - --cherry-pick \ - --right-only \ - --no-merges \ - --format='%h %s' \ - ${prevRelease}..HEAD - """ - ).trim() - } catch (Exception ex) { - println 'ERROR: Failed to retrieve changes.' - println ex.message - return ':warning: __Please fill me in!__' - } - /* remove single quotes to not confuse formatting */ - changes = changes.replace('\'', '') - return changes + '\nDiff:' + getDiffUrl(prevRelease, curRelease) -} - -def releaseExists(Map args) { - def output = sh( - returnStdout: true, - script: """ - github-release info --json \ - -u '${args.user}' \ - -r '${args.repo}' \ - | jq '.Releases[].tag_name' - """ - ) - return output.contains(args.version) -} - -def releaseDelete(Map args) { - if (!releaseExists(args)) { - return - } - sh """ - github-release delete \ - -u '${args.user}' \ - -r '${args.repo}' \ - -t '${args.version}' \ - """ -} - -def releaseUpload(Map args) { - args.files.each { - sh """ - github-release upload \ - -u '${args.user}' \ - -r '${args.repo}' \ - -t '${args.version}' \ - -n ${it} \ - -f ${it} - """ - } -} - -def releasePublish(Map args) { - sh """ - github-release release \ - -u '${args.user}' \ - -r '${args.repo}' \ - -n '${args.version}' \ - -t '${args.version}' \ - -c '${args.branch}' \ - -d '${args.desc}' \ - ${args.draft ? '--draft' : ''} - """ -} - -def publishRelease(Map args) { - def config = [ - draft: true, - user: 'status-im', - repo: 'status-react', - files: args.files, - version: args.version, - branch: ghcmgr.utils.branchName(), - desc: getReleaseChanges(), - ] - /* we release only for mobile right now */ - withCredentials([usernamePassword( - credentialsId: 'status-im-auto', - usernameVariable: 'GITHUB_USER', - passwordVariable: 'GITHUB_TOKEN' - )]) { - releaseDelete(config) /* so we can re-release it */ - releasePublish(config) - releaseUpload(config) - } -} - -def publishReleaseMobile(path='pkg') { - def found = findFiles(glob: "${path}/*") - if (found.size() == 0) { - sh "ls ${path}" - error("No file to release in ${path}") - } - publishRelease( - version: ghcmgr.utils.getVersion(), - files: found.collect { it.path }, - ) -} - -def notifyPR(success) { - if (ghcmgr.utils.changeId() == null) { return } - try { - ghcmgr.postBuild(success) - } catch (ex) { /* fallback to posting directly to GitHub */ - println "Failed to use GHCMGR: ${ex}" - switch (success) { - case true: notifyPRSuccess(); break - } - } -} - -return this diff --git a/ci/ios.groovy b/ci/ios.groovy deleted file mode 100644 index 9580ed1db4..0000000000 --- a/ci/ios.groovy +++ /dev/null @@ -1,102 +0,0 @@ -nix = load('ci/nix.groovy') -utils = load('ci/utils.groovy') - -def plutil(name, value) { - return """ - plutil -replace ${name} -string ${value} ios/StatusIm/Info.plist; -""" -} - -def bundle() { - def btype = utils.getBuildType() - def target - switch (btype) { - case 'release': target = 'release'; break; - case 'testflight': target = 'release'; break; - case 'e2e': target = 'e2e'; break; - default: target = 'nightly'; - } - /* configure build metadata */ - nix.shell(plutil('CFBundleShortVersionString', utils.getVersion()), attr: 'shells.ios') - nix.shell(plutil('CFBundleVersion', utils.genBuildNumber()), attr: 'shells.ios') - nix.shell(plutil('CFBundleBuildUrl', currentBuild.absoluteUrl), attr: 'shells.ios') - /* the dir might not exist */ - sh 'mkdir -p status-e2e' - /* build the actual app */ - withCredentials([ - string(credentialsId: "slave-pass-${env.NODE_NAME}", variable: 'KEYCHAIN_PASSWORD'), - string(credentialsId: 'fastlane-match-password', variable: 'MATCH_PASSWORD'), - usernamePassword( - credentialsId: 'fastlane-match-apple-id', - usernameVariable: 'FASTLANE_APPLE_ID', - passwordVariable: 'FASTLANE_PASSWORD' - ), - ]) { - nix.shell( - "bundle exec --gemfile=fastlane/Gemfile fastlane ios ${target}", - keepEnv: [ - 'FASTLANE_DISABLE_COLORS', - 'FASTLANE_PASSWORD', 'KEYCHAIN_PASSWORD', - 'MATCH_PASSWORD', 'FASTLANE_APPLE_ID', - ], - attr: 'shells.ios' - ) - } - /* rename built file for uploads and archivization */ - def pkg = '' - if (btype == 'release') { - pkg = utils.pkgFilename('release', 'ipa') - sh "cp status_appstore/StatusIm.ipa ${pkg}" - } else if (btype == 'e2e') { - pkg = utils.pkgFilename('e2e', 'app.zip') - sh "cp status-e2e/StatusIm.app.zip ${pkg}" - } else if (btype != 'testflight') { - pkg = utils.pkgFilename(btype, 'ipa') - sh "cp status-adhoc/StatusIm.ipa ${pkg}" - } - /* necessary for Diawi upload */ - env.DIAWI_IPA = pkg - return pkg -} - -def uploadToDiawi() { - withCredentials([ - string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'), - ]) { - /* This can silently fail with 'File is not processed.' */ - nix.shell( - 'bundle exec --verbose --gemfile=fastlane/Gemfile fastlane ios upload_diawi', - keepEnv: ['FASTLANE_DISABLE_COLORS', 'DIAWI_TOKEN'], - attr: 'shells.fastlane' - ) - } - diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out" - /* Save the URL in the build description */ - currentBuild.description = "Diawi Link" - return diawiUrl -} - -def uploadToSauceLabs() { - def changeId = utils.getParentRunEnv('CHANGE_ID') - if (changeId != null) { - env.SAUCE_LABS_NAME = "${changeId}.app.zip" - } else { - env.SAUCE_LABS_NAME = "im.status.ethereum-e2e-${utils.gitCommit()}.app.zip" - } - withCredentials([ - usernamePassword( - credentialsId: 'sauce-labs-api', - usernameVariable: 'SAUCE_USERNAME', - passwordVariable: 'SAUCE_ACCESS_KEY' - ), - ]) { - nix.shell( - 'bundle exec --gemfile=fastlane/Gemfile fastlane ios saucelabs', - keepEnv: ['FASTLANE_DISABLE_COLORS', 'SAUCE_ACCESS_KEY', 'SAUCE_USERNAME'], - attr: 'shells.fastlane' - ) - } - return env.SAUCE_LABS_NAME -} - -return this diff --git a/ci/jenkins.groovy b/ci/jenkins.groovy deleted file mode 100644 index 2d97a6fee1..0000000000 --- a/ci/jenkins.groovy +++ /dev/null @@ -1,92 +0,0 @@ -import jenkins.model.CauseOfInterruption.UserInterruption -import hudson.model.Result -import hudson.model.Run - -utils = load 'ci/utils.groovy' - -@NonCPS -def abortPreviousRunningBuilds() { - /* Aborting makes sense only for PR builds, since devs start so many of them */ - if (!env.JOB_NAME.contains('status-react/prs')) { - println ">> Not aborting any previous jobs. Not a PR build." - return - } - Run previousBuild = currentBuild.rawBuild.getPreviousBuildInProgress() - - while (previousBuild != null) { - if (previousBuild.isInProgress()) { - def executor = previousBuild.getExecutor() - if (executor != null) { - println ">> Aborting older build #${previousBuild.number}" - executor.interrupt(Result.ABORTED, new UserInterruption( - "newer build #${currentBuild.number}" - )) - } - } - - previousBuild = previousBuild.getPreviousBuildInProgress() - } -} - -def strParam(key, value) { - /* just a helper for making string params */ - return [ - name: key, - value: value, - $class: 'StringParameterValue', - ] -} - -def Build(name = null) { - /** - * Generate parameters to pass from current params - * This allows utils.updateEnv() to work in sub-jobs - **/ - parameters = params.keySet().collectEntries { key -> - [(key): strParam(key, params.get(key))] - } - /* default to current build type */ - parameters['BUILD_TYPE'] = strParam('BUILD_TYPE', utils.getBuildType()) - /* need to drop origin/ to match definitions of child jobs */ - parameters['BRANCH'] = strParam('BRANCH', utils.branchName()) - /* necessary for updating GitHub PRs */ - parameters['CHANGE_ID'] = strParam('CHANGE_ID', env.CHANGE_ID) - /* always pass the BRANCH and BUILD_TYPE params with current branch */ - def b = build( - job: name, - /* this allows us to analize the job even after failure */ - propagate: false, - parameters: parameters.values() - ) - /* BlueOcean seems to not show child-build links */ - println "Build: ${b.getAbsoluteUrl()} (${b.result})" - if (b.result != 'SUCCESS') { - error("Build Failed") - } - return b -} - -def copyArts(build) { - /** - * The build argument is of class RunWrapper. - * https://javadoc.jenkins.io/plugin/workflow-support/org/jenkinsci/plugins/workflow/support/steps/build/RunWrapper.html - **/ - copyArtifacts( - projectName: build.fullProjectName, - target: 'pkg', - flatten: true, - selector: specific("${build.number}") - ) -} - -def setBuildDesc(Map links) { - def desc = 'Links: \n' - links.each { type, url -> - if (url != null) { - desc += "${type} \n" - } - } - currentBuild.description = desc -} - -return this diff --git a/ci/nix.groovy b/ci/nix.groovy deleted file mode 100644 index caefd2fa6c..0000000000 --- a/ci/nix.groovy +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Arguments: - * - pure - Use --pure mode with Nix for more deterministic behaviour - * - args - Map of arguments to provide to --argstr - * - keepEnv - List of env variables to keep even in pure mode - **/ -def shell(Map opts = [:], String cmd) { - def defaults = [ - pure: true, - args: ['target': env.TARGET ? env.TARGET : 'default'], - keepEnv: ['LOCALE_ARCHIVE_2_27'], - sandbox: true, - ] - /* merge defaults with received opts */ - opts = defaults + opts - /* previous merge overwrites the array */ - opts.keepEnv = (opts.keepEnv + defaults.keepEnv).unique() - /* not all targets can use a pure build */ - if (env.TARGET in ['windows', 'ios']) { - opts.pure = false - } - sh(""" - set +x - . ~/.nix-profile/etc/profile.d/nix.sh - set -x - nix-shell --run \'${cmd}\' ${_getNixCommandArgs(opts, true)} - """) -} - -/** - * Arguments: - * - pure - Use --pure mode with Nix for more deterministic behaviour - * - link - Bu default build creates a `result` directory, you can turn that off - * - conf - Map of config values to provide to --arg config - * - args - Map of arguments to provide to --argstr - * - attr - Name of attribute to use with --attr flag - * - keepEnv - List of env variables to pass through to Nix build - * - safeEnv - Name of env variables to pass securely through to Nix build (they won't get captured in Nix derivation file) - * - sandbox - If build process should run inside of a sandbox - * - sandboxPaths - List of file paths to make available in Nix sandbox - **/ -def build(Map opts = [:]) { - def defaults = [ - pure: true, - link: true, - args: ['target': env.TARGET], - conf: [:], - attr: null, - keepEnv: [], - safeEnv: [], - sandbox: true, - sandboxPaths: [], - ] - /* merge defaults with received opts */ - opts = defaults + opts - /* Previous merge overwrites the array */ - opts.args = defaults.args + opts.args - opts.keepEnv = (opts.keepEnv + defaults.keepEnv).unique() - - def nixPath = sh( - returnStdout: true, - script: """ - set +x - . ~/.nix-profile/etc/profile.d/nix.sh - set -x - nix-build ${_getNixCommandArgs(opts, false)} - """ - ).trim() - /* if not linking, copy results, but only if there's just one path */ - if (!opts.link && nixPath && !nixPath.contains('\n')) { - copyResults(nixPath) - } - return nixPath -} - -private def copyResults(path) { - def resultsPath = "${env.WORKSPACE}/result" - sh "rm -fr ${resultsPath}" - sh "mkdir -p ${resultsPath}" - sh "cp -fr ${path}/* ${resultsPath}/" - sh "chmod -R 755 ${resultsPath}" -} - -private makeNixBuildEnvFile(Map opts = [:]) { - File envFile = File.createTempFile("nix-env", ".tmp") - if (!opts.safeEnv.isEmpty()) { - // Export the environment variables we want to keep into a temporary script we can pass to Nix and source it from the build script - def exportCommandList = opts.safeEnv.collect { envVarName -> """ - echo \"export ${envVarName}=\\\"\$(printenv ${envVarName})\\\"\" >> ${envFile.absolutePath} - """ } - def exportCommands = exportCommandList.join("") - sh """ - ${exportCommands} - chmod u+x ${envFile.absolutePath} - """ - - opts.args = opts.args + [ 'secrets-file': envFile.absolutePath ] - opts.sandboxPaths = opts.sandboxPaths + envFile.absolutePath - } - - return envFile -} - -private def _getNixCommandArgs(Map opts = [:], boolean isShell) { - def keepFlags = [] - def entryPoint = "\'${env.WORKSPACE}/shell.nix\'" - if (!isShell || opts.attr != null) { - entryPoint = "\'${env.WORKSPACE}/default.nix\'" - } - /* don't let nix.conf control sandbox status */ - def extraSandboxPathsFlag = "--option sandbox ${opts.sandbox}" - - if (isShell) { - keepFlags = opts.keepEnv.collect { var -> "--keep ${var} " } - } else { - def envVarsList = opts.keepEnv.collect { var -> "${var}=\"${env[var]}\";" } - keepFlags = ["--arg env \'{${envVarsList.join("")}}\'"] - - /* Export the environment variables we want to keep into - * a Nix attribute set we can pass to Nix and source it from the build script */ - def envFile = makeNixBuildEnvFile(opts) - envFile.deleteOnExit() - } - - def configFlag = '' - def argsFlags = opts.args.collect { key,val -> "--argstr ${key} \'${val}\'" } - def attrFlag = '' - if (opts.attr != null) { - attrFlag = "--attr '${opts.attr}'" - } - if (opts.conf != null && opts.conf != [:]) { - def configFlags = opts.conf.collect { key,val -> "${key}=\"${val}\";" } - configFlag = "--arg config \'{${configFlags.join('')}}\'" - } - if (opts.sandboxPaths != null && !opts.sandboxPaths.isEmpty()) { - extraSandboxPathsFlag += " --option extra-sandbox-paths \"${opts.sandboxPaths.join(' ')}\"" - } - - return [ - opts.pure ? "--pure" : "", - opts.link ? "" : "--no-out-link", - configFlag, - keepFlags.join(" "), - argsFlags.join(" "), - extraSandboxPathsFlag, - attrFlag, - entryPoint, - ].join(" ") -} - -def prepEnv() { - if (env.TARGET in ['linux', 'windows', 'android']) { - def glibcLocales = sh( - returnStdout: true, - script: """ - . ~/.nix-profile/etc/profile.d/nix.sh && \\ - nix-build --no-out-link '' -A glibcLocales - """ - ).trim() - /** - * This is a hack to fix missing locale errors. - * See: - * - https://github.com/NixOS/nixpkgs/issues/38991 - * - https://qiita.com/kimagure/items/4449ceb0bda5c10ca50f - **/ - env.LOCALE_ARCHIVE_2_27 = "${glibcLocales}/lib/locale/locale-archive" - } -} - -return this diff --git a/ci/utils.groovy b/ci/utils.groovy deleted file mode 100644 index ebaf7cf7e2..0000000000 --- a/ci/utils.groovy +++ /dev/null @@ -1,151 +0,0 @@ -def getVersion() { - def path = "${env.WORKSPACE}/VERSION" - return readFile(path).trim() -} - -def branchName() { - return env.GIT_BRANCH.replaceAll(/.*origin\//, '') -} - -def parentOrCurrentBuild() { - def c = currentBuild.rawBuild.getCause(hudson.model.Cause$UpstreamCause) - if (c == null) { return currentBuild } - return c.getUpstreamRun() -} - -def timestamp() { - /* we use parent if available to make timestmaps consistent */ - def now = new Date(parentOrCurrentBuild().timeInMillis) - return now.format('yyMMdd-HHmmss', TimeZone.getTimeZone('UTC')) -} - -def gitCommit() { - return GIT_COMMIT.take(6) -} - -def pkgFilename(type, ext, arch=null) { - /* the grep removes the null arch */ - return [ - "StatusIm", timestamp(), gitCommit(), type, arch, - ].grep().join('-') + ".${ext}" -} - -def doGitRebase() { - if (!isPRBuild()) { - println 'Skipping rebase due for non-PR build...' - return - } - def rebaseBranch = 'develop' - if (env.CHANGE_TARGET) { /* This is available for PR builds */ - rebaseBranch = env.CHANGE_TARGET - } - sh 'git status' - sh "git fetch --force origin ${rebaseBranch}:${rebaseBranch}" - try { - sh "git rebase ${rebaseBranch}" - } catch (e) { - sh 'git rebase --abort' - throw e - } -} - -def genBuildNumber() { - def number = sh( - returnStdout: true, - script: "${env.WORKSPACE}/scripts/version/gen_build_no.sh" - ).trim() - println "Build Number: ${number}" - return number -} - -def readBuildNumber() { - def number = sh( - returnStdout: true, - script: "${env.WORKSPACE}/scripts/version/build_no.sh" - ).trim() - return number -} - -def getDirPath(path) { - return path.tokenize('/')[0..-2].join('/') -} - -def getFilename(path) { - return path.tokenize('/')[-1] -} - -def getEnv(build, envvar) { - return build.getBuildVariables().get(envvar) -} - -def buildDuration() { - def duration = currentBuild.durationString - return '~' + duration.take(duration.lastIndexOf(' and counting')) -} - -def pkgFind(glob) { - def fullGlob = "pkg/*${glob}" - def found = findFiles(glob: fullGlob) - if (found.size() == 0) { - sh 'ls -l pkg/' - error("File not found via glob: ${fullGlob} ${found.size()}") - } - return found[0].path -} - -def isPRBuild() { - return env.JOB_NAME.startsWith('status-react/prs') -} - -def isE2EBuild() { - return env.JOB_NAME.contains('e2e') -} - -def getBuildType() { - def jobName = env.JOB_NAME - if (isE2EBuild()) { - return 'e2e' - } - if (isPRBuild()) { - return 'pr' - } - if (jobName.startsWith('status-react/nightly')) { - return 'nightly' - } - if (jobName.startsWith('status-react/release')) { - return 'release' - } - return params.BUILD_TYPE -} - -def getParentRunEnv(name) { - def c = currentBuild.rawBuild.getCause(hudson.model.Cause$UpstreamCause) - if (c == null) { return null } - return c.getUpstreamRun().getEnvironment()[name] -} - -def changeId() { - /* CHANGE_ID can be provided via the build parameters or from parent */ - def changeId = env.CHANGE_ID - changeId = params.CHANGE_ID ? params.CHANGE_ID : changeId - changeId = getParentRunEnv('CHANGE_ID') ? getParentRunEnv('CHANGE_ID') : changeId - if (!changeId) { - println('This build is not related to a PR, CHANGE_ID missing.') - println('GitHub notification impossible, skipping...') - return null - } - return changeId -} - -def updateEnv(type) { - def envFile = "${env.WORKSPACE}/.env.jenkins" - /* select .env based on type of build */ - if (['nightly', 'release', 'e2e'].contains(type)) { - envFile = "${env.WORKSPACE}/.env.${type}" - } - sh "ln -sf ${envFile} .env" - /* show contents for debugging purposes */ - sh "cat ${envFile}" -} - -return this diff --git a/scripts/generate-keystore.sh b/scripts/generate-keystore.sh index 84a5e56b26..775d80e3d7 100755 --- a/scripts/generate-keystore.sh +++ b/scripts/generate-keystore.sh @@ -22,7 +22,7 @@ if [[ -z ${KEYSTORE_PATH} ]]; then KEYSTORE_PATH=$(property_gradle 'KEYSTORE_FILE') fi # Replace ~ with proper absolute path -KEYSTORE_PATH=${KEYSTORE_FILE/#\~/$HOME} +KEYSTORE_PATH=${KEYSTORE_PATH/#\~/$HOME} if [[ -e "${KEYSTORE_PATH}" ]]; then echo -e "${YLW}Keystore file already exists:${RST} ${KEYSTORE_PATH}" > /dev/stderr