From 9f601e851d46ccd4ffafa7ccd10fc3c4e1491229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Soko=C5=82owski?= Date: Thu, 28 Feb 2019 11:56:58 +0100 Subject: [PATCH] add jenkinsfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jakub SokoĊ‚owski --- ci/Jenkinsfile.android | 16 ++++------ ci/Jenkinsfile.ios | 4 +-- ci/Jenkinsfile.linux | 13 ++++---- ci/Jenkinsfile.macos | 9 +++--- ci/Jenkinsfile.nix.linux | 63 ++++++++++++++++++++++++++++++++++++++ ci/Jenkinsfile.nix.macos | 65 ++++++++++++++++++++++++++++++++++++++++ ci/Jenkinsfile.windows | 10 ++----- ci/android.groovy | 8 ++--- ci/common.groovy | 10 +++++++ ci/desktop.groovy | 24 ++++++++------- ci/ghcmgr.groovy | 4 +-- ci/github.groovy | 2 +- ci/ios.groovy | 6 ++-- ci/mobile.groovy | 4 +-- ci/utils.groovy | 12 ++++++-- default.nix | 2 ++ scripts/setup | 2 +- 17 files changed, 200 insertions(+), 54 deletions(-) create mode 100644 ci/Jenkinsfile.nix.linux create mode 100644 ci/Jenkinsfile.nix.macos diff --git a/ci/Jenkinsfile.android b/ci/Jenkinsfile.android index bff729c1be..7421bed8d9 100644 --- a/ci/Jenkinsfile.android +++ b/ci/Jenkinsfile.android @@ -2,8 +2,7 @@ pipeline { agent { docker { label 'linux' - /* WARNING: remember to keep this up-to-date with the value in docker/android/Makefile */ - image 'statusteam/status-build-android:1.1.2-9ea4e0f4' + image 'statusteam/nix:jenkins' args ( "-v /home/jenkins/tmp:/var/tmp:rw "+ "-v /home/jenkins/status-im.keystore:/tmp/status-im.keystore:ro" @@ -38,13 +37,10 @@ pipeline { LC_ALL = 'en_US.UTF-8' FASTLANE_DISABLE_COLORS = 1 REALM_DISABLE_ANALYTICS = 1 + CI_ENVIRONMENT = 'jenkins' /* since we are mounting it we need to specify location */ STATUS_RELEASE_STORE_FILE = '/tmp/status-im.keystore' - ANDROID_HOME = '/usr/lib/android-sdk' - ANDROID_SDK_ROOT = '/usr/lib/android-sdk' - ANDROID_NDK = '/usr/lib/android-ndk' - ANDROID_NDK_HOME = '/usr/lib/android-ndk' - /* bundle cache is sensitive to being used by differnt ruby versions */ + /* bundle cache is sensitive to being used by different ruby versions */ BUNDLE_PATH = "/tmp/bundle" /* We use EXECUTOR_NUMBER to avoid multiple instances clashing */ LEIN_HOME = "/var/tmp/lein-${EXECUTOR_NUMBER}" @@ -69,17 +65,17 @@ pipeline { } stage('Lint') { steps { - sh 'lein cljfmt check' + script { cmn.utils.nix_sh('lein cljfmt check') } } } stage('Tests') { steps { - sh 'lein test-cljs' + script { cmn.utils.nix_sh('lein test-cljs') } } } stage('Build') { steps { - sh "lein prod-build-android" + script { cmn.utils.nix_sh('lein prod-build-android') } } } stage('Bundle') { diff --git a/ci/Jenkinsfile.ios b/ci/Jenkinsfile.ios index 89531e7c5e..5911cd9346 100644 --- a/ci/Jenkinsfile.ios +++ b/ci/Jenkinsfile.ios @@ -52,12 +52,12 @@ pipeline { } stage('Lint') { steps {nvm(env.NODE_VERSION) { - sh 'lein cljfmt check' + script { cmn.utils.nix_sh('lein cljfmt check') } } } } stage('Tests') { steps { nvm(env.NODE_VERSION) { - sh 'lein test-cljs' + script { cmn.utils.nix_sh('lein test-cljs') } } } } stage('Build') { diff --git a/ci/Jenkinsfile.linux b/ci/Jenkinsfile.linux index 8e09e874b6..64ce66d7ce 100644 --- a/ci/Jenkinsfile.linux +++ b/ci/Jenkinsfile.linux @@ -1,11 +1,10 @@ pipeline { agent { - /* privileged mode is necessary for fuse */ docker { label 'linux' - /* WARNING: remember to keep this up-to-date with the value in docker/linux/Makefile */ - image 'statusteam/status-build-linux:1.1.1-8e5f6658' + image 'statusteam/nix:jenkins' args ( + "-v /tmp/Android/Sdk:/home/jenkins/.status/Android/Sdk:rw "+ "-v /var/tmp/lein:/var/tmp/lein:rw "+ "-v /var/tmp/npm:/var/tmp/npm:rw "+ "-v /opt/desktop-files:/opt/desktop-files:rw" @@ -43,8 +42,9 @@ pipeline { LANGUAGE = 'en_US.UTF-8' LC_ALL = 'en_US.UTF-8' NPM_CONFIG_CACHE = '/var/tmp/npm' + CI_ENVIRONMENT = 'jenkins' LEIN_HOME = '/var/tmp/lein' - QT_PATH = '/opt/qt' + NIX_CONF_DIR = "${env.WORKSPACE}/scripts/lib/setup/nix" STATUSIM_APPIMAGE_DIR = '/opt/desktop-files' VERBOSE_LEVEL = '3' } @@ -60,18 +60,19 @@ pipeline { print "Running ${btype} build!" cmn.ci.abortPreviousRunningBuilds() /* Cleanup and Prep */ + cmn.prepNixEnvironment() desktop.prepDeps() } } } stage('Lint') { steps { - sh 'lein cljfmt check' + script { cmn.utils.nix_sh('lein cljfmt check') } } } stage('Tests') { steps { - sh 'lein test-cljs' + script { cmn.utils.nix_sh('lein test-cljs') } } } stage('Build') { diff --git a/ci/Jenkinsfile.macos b/ci/Jenkinsfile.macos index 684647345c..11216a03a4 100644 --- a/ci/Jenkinsfile.macos +++ b/ci/Jenkinsfile.macos @@ -45,6 +45,7 @@ pipeline { /* Read the valid NodeJS version */ env.NODE_VERSION = cmn.utils.getToolVersion('node') /* Cleanup and Prep */ + cmn.prepNixEnvironment() nvm(env.NODE_VERSION) { desktop.prepDeps() } @@ -53,12 +54,12 @@ pipeline { } stage('Lint') { steps { nvm(env.NODE_VERSION) { - sh 'lein cljfmt check' + script { cmn.utils.nix_sh('lein cljfmt check') } } } } stage('Tests') { steps { nvm(env.NODE_VERSION) { - sh 'lein test-cljs' + script { cmn.utils.nix_sh('lein test-cljs') } } } } stage('Build') { @@ -72,9 +73,9 @@ pipeline { } } stage('Bundle') { - steps { nvm(env.NODE_VERSION) { + steps { script { dmg = desktop.bundleMacOS(btype) } - } } + } } stage('Archive') { steps { diff --git a/ci/Jenkinsfile.nix.linux b/ci/Jenkinsfile.nix.linux new file mode 100644 index 0000000000..65003fdcf1 --- /dev/null +++ b/ci/Jenkinsfile.nix.linux @@ -0,0 +1,63 @@ +pipeline { + agent { + /* the -u is necessary for acces to /nix */ + docker { + label 'linux' + image 'statusteam/nix:jenkins' + } + } + + environment { + /* we source .bash_profile to be able to use nix-store */ + NIX_SSHOPTS="-o StrictHostKeyChecking=no source .bash_profile;" + /* where our /nix/store is hosted */ + NIX_CACHE_USER = 'nix-cache' + NIX_CACHE_HOST = 'master-01.do-ams3.ci.misc.statusim.net' + /* we add both keys so default binary cache also works */ + NIX_BIN_CACHE = 'https://nix-cache.status.im/' + NIX_BIN_CACHE_KEYS = ( + 'nix-cache.status.im-1:x/93lOfLU+duPplwMSBR+OlY4+mo+dCN7n0mr4oPwgY= '+ + 'cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' + ) + } + + options { + timestamps() + disableConcurrentBuilds() + /* Prevent Jenkins jobs from running forever */ + timeout(time: 20, unit: 'MINUTES') + /* Limit builds retained */ + buildDiscarder(logRotator( + numToKeepStr: '20', + daysToKeepStr: '30', + )) + } + + stages { + stage('Setup') { + steps { + sh 'nix-env -i openssh' + } + } + stage('Build') { + steps { + /* we dogfood our own cache to speed up builds */ + sh """ + nix-build -A env.all \ + --option extra-substituters '${NIX_BIN_CACHE}' \ + --trusted-public-keys '${NIX_BIN_CACHE_KEYS}' + """ + } + } + stage('Upload') { + steps { + sshagent(credentials: ['nix-cache-ssh']) { + sh """ + find /nix/store/ -mindepth 1 -maxdepth 1 -not -name '.links' -and -not -name '*.lock' | \ + xargs nix-copy-closure -v --to ${NIX_CACHE_USER}@${NIX_CACHE_HOST} + """ + } + } + } + } +} diff --git a/ci/Jenkinsfile.nix.macos b/ci/Jenkinsfile.nix.macos new file mode 100644 index 0000000000..26855f040a --- /dev/null +++ b/ci/Jenkinsfile.nix.macos @@ -0,0 +1,65 @@ +pipeline { + agent { + label 'macos' + } + + environment { + /* we source .bash_profile to be able to use nix-store */ + NIX_SSHOPTS="-o StrictHostKeyChecking=no source .bash_profile;" + /* where our /nix/store is hosted */ + NIX_CACHE_USER = 'nix-cache' + NIX_CACHE_HOST = 'master-01.do-ams3.ci.misc.statusim.net' + /* we add both keys so default binary cache also works */ + NIX_BIN_CACHE = 'https://nix-cache.status.im/' + NIX_BIN_CACHE_KEYS = ( + 'nix-cache.status.im-1:x/93lOfLU+duPplwMSBR+OlY4+mo+dCN7n0mr4oPwgY= '+ + 'cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' + ) + } + + options { + timestamps() + disableConcurrentBuilds() + /* Prevent Jenkins jobs from running forever */ + timeout(time: 120, unit: 'MINUTES') + /* Limit builds retained */ + buildDiscarder(logRotator( + numToKeepStr: '20', + daysToKeepStr: '30', + )) + } + + stages { + stage('Setup') { + steps { + sh 'make setup' + sh """ + . ~/.nix-profile/etc/profile.d/nix.sh && \ + nix-env -i openssh + """ + } + } + stage('Build') { + steps { + /* we dogfood our own cache to speed up builds */ + sh """ + . ~/.nix-profile/etc/profile.d/nix.sh && \ + nix-build -A env.all \ + --option extra-substituters '${NIX_BIN_CACHE}' \ + --trusted-public-keys '${NIX_BIN_CACHE_KEYS}' + """ + } + } + stage('Upload') { + steps { + sshagent(credentials: ['nix-cache-ssh']) { + sh """ + . ~/.nix-profile/etc/profile.d/nix.sh && \ + find /nix/store/ -mindepth 1 -maxdepth 1 -not -name '.links' -and -not -name '*.lock' | \ + xargs nix-copy-closure -v --to ${NIX_CACHE_USER}@${NIX_CACHE_HOST} + """ + } + } + } + } +} diff --git a/ci/Jenkinsfile.windows b/ci/Jenkinsfile.windows index 2b1c7451a7..2fd1c29c8a 100644 --- a/ci/Jenkinsfile.windows +++ b/ci/Jenkinsfile.windows @@ -1,13 +1,9 @@ pipeline { agent { - /* privileged mode is necessary for fuse */ docker { label 'linux-new' - /* WARNING: remember to keep this up-to-date with the value in docker/windows/Makefile */ - image 'statusteam/status-build-windows:1.1.1-67cf7368' + image 'statusteam/nix:jenkins' args ( - "--privileged "+ - "-v /dev/fuse:/dev/fuse "+ "-v /var/tmp/lein:/var/tmp/lein:rw "+ "-v /var/tmp/npm:/var/tmp/npm:rw " ) @@ -69,12 +65,12 @@ pipeline { } stage('Lint') { steps { - sh 'lein cljfmt check' + script { cmn.utils.nix_sh('lein cljfmt check') } } } stage('Tests') { steps { - sh 'lein test-cljs' + script { cmn.utils.nix_sh('lein test-cljs') } } } stage('Build') { diff --git a/ci/android.groovy b/ci/android.groovy index 47937131f7..a4997b5995 100644 --- a/ci/android.groovy +++ b/ci/android.groovy @@ -18,7 +18,7 @@ def bundle(type = 'nightly') { passwordVariable: 'STATUS_RELEASE_KEY_PASSWORD' ) ]) { - sh "./gradlew assembleRelease ${gradleOpt}" + utils.nix_sh "./gradlew assembleRelease ${gradleOpt}" } } def pkg = utils.pkgFilename(type, 'apk') @@ -32,7 +32,7 @@ def uploadToPlayStore(type = 'nightly') { withCredentials([ string(credentialsId: "SUPPLY_JSON_KEY_DATA", variable: 'GOOGLE_PLAY_JSON_KEY'), ]) { - sh "bundle exec fastlane android ${type}" + utils.nix_sh "bundle exec fastlane android ${type}" } } @@ -48,7 +48,7 @@ def uploadToSauceLabs() { string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'), string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'), ]) { - sh 'bundle exec fastlane android saucelabs' + utils.nix_sh 'bundle exec fastlane android saucelabs' } return env.SAUCE_LABS_NAME } @@ -58,7 +58,7 @@ def uploadToDiawi() { withCredentials([ string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'), ]) { - sh 'bundle exec fastlane android upload_diawi' + utils.nix_sh 'bundle exec fastlane android upload_diawi' } diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out" return diawiUrl diff --git a/ci/common.groovy b/ci/common.groovy index 9f2a61e789..8b3ac86451 100644 --- a/ci/common.groovy +++ b/ci/common.groovy @@ -42,4 +42,14 @@ def notifyPR(success) { } } +def prepNixEnvironment() { + if (env.TARGET_PLATFORM == 'linux' || env.TARGET_PLATFORM == 'windows' || env.TARGET_PLATFORM == 'android') { + def glibcLocales = sh( + returnStdout: true, + script: ". ~/.nix-profile/etc/profile.d/nix.sh && nix-build --no-out-link '' -A glibcLocales" + ).trim() + env.LOCALE_ARCHIVE_2_27 = "${glibcLocales}/lib/locale/locale-archive" + } +} + return this diff --git a/ci/desktop.groovy b/ci/desktop.groovy index 7459558de3..c949ca2fe0 100644 --- a/ci/desktop.groovy +++ b/ci/desktop.groovy @@ -6,13 +6,15 @@ packageFolder = './StatusImPackage' def cleanupAndDeps() { sh 'make clean' sh 'cp .env.jenkins .env' - sh 'lein deps' + utils.nix_sh 'lein deps' utils.installJSDeps('desktop') } def buildClojureScript() { - sh 'make prod-build-desktop' - sh './scripts/build-desktop.sh buildClojureScript' + utils.nix_sh ''' + make prod-build-desktop && \ + ./scripts/build-desktop.sh buildClojureScript + ''' } def uploadArtifact(filename) { @@ -54,13 +56,13 @@ def compile() { if (env.QT_PATH) { env.PATH = "${env.QT_PATH}:${env.PATH}" } - sh './scripts/build-desktop.sh compile' + utils.nix_sh './scripts/build-desktop.sh compile' } def bundleWindows(type = 'nightly') { def pkg - sh './scripts/build-desktop.sh bundle' + utils.nix_sh './scripts/build-desktop.sh bundle' dir(packageFolder) { pkg = utils.pkgFilename(type, 'exe') sh "mv ../Status-x86_64-setup.exe ${pkg}" @@ -70,7 +72,7 @@ def bundleWindows(type = 'nightly') { def bundleLinux(type = 'nightly') { def pkg - sh './scripts/build-desktop.sh bundle' + utils.nix_sh './scripts/build-desktop.sh bundle' dir(packageFolder) { pkg = utils.pkgFilename(type, 'AppImage') sh "mv ../Status-x86_64.AppImage ${pkg}" @@ -80,16 +82,18 @@ def bundleLinux(type = 'nightly') { def bundleMacOS(type = 'nightly') { def pkg = utils.pkgFilename(type, 'dmg') - sh './scripts/build-desktop.sh bundle' + utils.nix_sh './scripts/build-desktop.sh bundle' 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') ]) { - sh '../scripts/sign-macos-pkg.sh Status.app ../deployment/macos/macos-developer-id.keychain-db.gpg' - sh "../node_modules/appdmg/bin/appdmg.js ../deployment/macos/status-dmg.json ${pkg}" - sh "../scripts/sign-macos-pkg.sh ${pkg} ../deployment/macos/macos-developer-id.keychain-db.gpg" + utils.nix_sh """ + ../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 + """ } } return "${packageFolder}/${pkg}".drop(2) diff --git a/ci/ghcmgr.groovy b/ci/ghcmgr.groovy index 97d47e0cf9..af046460c4 100644 --- a/ci/ghcmgr.groovy +++ b/ci/ghcmgr.groovy @@ -11,7 +11,7 @@ utils = load 'ci/utils.groovy' def buildObj(success) { def pkg_url = env.PKG_URL /* a bare ipa file is not installable on iOS */ - if (env.BUILD_PLATFORM == 'ios') { + if (env.TARGET_PLATFORM == 'ios') { pkg_url = env.DIAWI_URL } /* assemble build object valid for ghcmgr */ @@ -19,7 +19,7 @@ def buildObj(success) { id: env.BUILD_DISPLAY_NAME, commit: GIT_COMMIT.take(8), success: success != null ? success : true, - platform: env.BUILD_PLATFORM + (utils.getBuildType() == 'e2e' ? '-e2e' : ''), + platform: env.TARGET_PLATFORM + (utils.getBuildType() == 'e2e' ? '-e2e' : ''), duration: utils.buildDuration(), url: currentBuild.absoluteUrl, pkg_url: pkg_url, diff --git a/ci/github.groovy b/ci/github.groovy index 2cd121fd32..480570c2af 100644 --- a/ci/github.groovy +++ b/ci/github.groovy @@ -62,7 +62,7 @@ def notifyPRSuccess() { def type = utils.getBuildType() == 'e2e' ? ' e2e' : '' msg += "[${env.JOB_NAME}${currentBuild.displayName}](${currentBuild.absoluteUrl}) ${d} " msg += "${utils.buildDuration()} ${d} ${GIT_COMMIT.take(8)} ${d} " - msg += "[:package: ${env.BUILD_PLATFORM}${type} package](${env.PKG_URL})" + msg += "[:package: ${env.TARGET_PLATFORM}${type} package](${env.PKG_URL})" notify(msg) } diff --git a/ci/ios.groovy b/ci/ios.groovy index 989de28cf9..990854b916 100644 --- a/ci/ios.groovy +++ b/ci/ios.groovy @@ -1,7 +1,7 @@ utils = load('ci/utils.groovy') def plutil(name, value) { - sh "plutil -replace ${name} -string ${value} ios/StatusIm/Info.plist" + utils.nix_sh "plutil -replace ${name} -string ${value} ios/StatusIm/Info.plist" } def bundle(type) { @@ -51,7 +51,7 @@ def uploadToDiawi() { withCredentials([ string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'), ]) { - sh 'bundle exec fastlane ios upload_diawi' + utils.nix_sh 'bundle exec fastlane ios upload_diawi' } diawiUrl = readFile "${env.WORKSPACE}/fastlane/diawi.out" return diawiUrl @@ -68,7 +68,7 @@ def uploadToSauceLabs() { string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'), string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'), ]) { - sh 'bundle exec fastlane ios saucelabs' + utils.nix_sh 'bundle exec fastlane ios saucelabs' } return env.SAUCE_LABS_NAME } diff --git a/ci/mobile.groovy b/ci/mobile.groovy index 7a765fddcb..c483aa51af 100644 --- a/ci/mobile.groovy +++ b/ci/mobile.groovy @@ -22,9 +22,9 @@ def prep(type = 'nightly') { sh 'cp .env.jenkins .env'; break } /* install ruby dependencies */ - sh 'bundle install --quiet' + utils.nix_sh 'bundle install --quiet' /* node deps, pods, and status-go download */ - sh "make prepare-${env.BUILD_PLATFORM}" + utils.nix_sh "make prepare-${env.TARGET_PLATFORM}" } return this diff --git a/ci/utils.groovy b/ci/utils.groovy index d59895ff6f..3fbb019745 100644 --- a/ci/utils.groovy +++ b/ci/utils.groovy @@ -15,6 +15,14 @@ def getToolVersion(name) { return version } +def nix_sh(cmd) { + sh """ + . ~/.nix-profile/etc/profile.d/nix.sh && \\ + nix-shell \'${env.WORKSPACE}/default.nix\' --argstr target-os \'${env.TARGET_PLATFORM}\' \\ + --run \'${cmd}\' + """ +} + def branchName() { return env.GIT_BRANCH.replaceAll(/.*origin\//, '') } @@ -91,10 +99,10 @@ def installJSDeps(platform) { def maxAttempts = 10 def installed = false /* prepare environment for specific platform build */ - sh "scripts/prepare-for-platform.sh ${platform}" + nix_sh "scripts/prepare-for-platform.sh ${platform}" while (!installed && attempt <= maxAttempts) { println "#${attempt} attempt to install npm deps" - sh 'yarn install --frozen-lockfile' + nix_sh 'yarn install --frozen-lockfile' installed = fileExists('node_modules/web3/index.js') attemp = attempt + 1 } diff --git a/default.nix b/default.nix index 1fc3372bbc..007ae14f5e 100644 --- a/default.nix +++ b/default.nix @@ -26,8 +26,10 @@ in with pkgs; name = "env"; env = buildEnv { name = name; paths = buildInputs; }; buildInputs = with _stdenv; [ + bash clojure curl + git jq leiningen lsof # used in scripts/start-react-native.sh diff --git a/scripts/setup b/scripts/setup index f4c150b1c7..e4826fbf7c 100755 --- a/scripts/setup +++ b/scripts/setup @@ -40,7 +40,7 @@ if ! program_exists "curl"; then exit 1 fi -if is_linux; then +if is_linux && [ -z "$CI_ENVIRONMENT" ]; then watches=$(cat /proc/sys/fs/inotify/max_user_watches) required_watches=524288 if [ $watches -lt $required_watches ]; then