add an e2e build target for ios

Squashe commits:
- add an e2e build target for ios
- add correct sdk and destination for simulator
- fixup! add correct sdk and destination for simulator
- drop xcarchive_path since we are not using it
- temporarily bind ios build to macos-03
- Detect installed simulator SDK and use it.
- Signed-off-by: Jakub Sokołowski <jakub@status.im>

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2018-11-19 18:37:04 +01:00
parent 9e75b89d58
commit a91a799eb5
No known key found for this signature in database
GPG Key ID: 4EF064D0E6D63020
8 changed files with 151 additions and 47 deletions

1
.gitignore vendored
View File

@ -91,6 +91,7 @@ status-go-*.zip
ios/Pods
ios/StatusIm.xcworkspace
.ruby-version
status-e2e/
#python
*.pyc

View File

@ -26,45 +26,44 @@ pipeline {
}
stage('Build') {
parallel {
stage('MacOS') {
when { expression { btype != 'release' } }
stage('MacOS') { when { expression { btype != 'release' } }
steps { script {
osx = cmn.buildBranch('status-react/combined/desktop-macos')
} } }
stage('Linux') {
when { expression { btype != 'release' } }
stage('Linux') { when { expression { btype != 'release' } }
steps { script {
nix = cmn.buildBranch('status-react/combined/desktop-linux')
} } }
stage('Windows') {
when { expression { btype != 'release' } }
stage('Windows') { when { expression { btype != 'release' } }
steps { script {
win = cmn.buildBranch('status-react/combined/desktop-windows')
} } }
stage('iOS') { steps { script {
ios = cmn.buildBranch('status-react/combined/mobile-ios')
} } }
stage('iOS e2e') { steps { script {
iose2e = cmn.buildBranch('status-react/combined/mobile-ios', 'e2e')
} } }
stage('Android') { steps { script {
dro = cmn.buildBranch('status-react/combined/mobile-android')
apk = cmn.buildBranch('status-react/combined/mobile-android')
} } }
stage('Android e2e') { steps { script {
e2e = cmn.buildBranch('status-react/combined/mobile-android', 'e2e')
apke2e = cmn.buildBranch('status-react/combined/mobile-android', 'e2e')
} } }
}
}
stage('Archive') {
steps { script {
sh('rm -f pkg/*')
if (btype != 'release') {
cmn.copyArts('status-react/combined/desktop-macos', osx.number)
cmn.copyArts('status-react/combined/desktop-linux', nix.number)
cmn.copyArts('status-react/combined/desktop-windows', win.number)
}
cmn.copyArts('status-react/combined/mobile-ios', ios.number)
cmn.copyArts('status-react/combined/mobile-android', dro.number)
cmn.copyArts('status-react/combined/mobile-android', e2e.number)
cmn.copyArts('status-react/combined/mobile-ios', ios.number)
cmn.copyArts('status-react/combined/mobile-ios', iose2e.number)
cmn.copyArts('status-react/combined/mobile-android', apk.number)
cmn.copyArts('status-react/combined/mobile-android', apke2e.number)
dir('pkg') {
/* generate sha256 checksums for upload */
sh "sha256sum * | tee ${cmn.pkgFilename(btype, 'sha256')}"
@ -74,7 +73,7 @@ pipeline {
}
stage('Upload') {
steps { script {
e2eUrl = cmn.uploadArtifact(cmn.pkgFind('e2e.apk'))
apke2eUrl = cmn.uploadArtifact(cmn.pkgFind('e2e.apk'))
apkUrl = cmn.uploadArtifact(cmn.pkgFind("${btype}.apk"))
if (btype != 'release') {
dmgUrl = cmn.uploadArtifact(cmn.pkgFind('dmg'))
@ -85,18 +84,23 @@ pipeline {
appUrl = null
exeUrl = null
}
iose2eUrl = cmn.uploadArtifact(cmn.pkgFind("e2e.app.zip"))
/* special case for iOS Diawi links */
ipaUrl = ios.getBuildVariables().get('DIAWI_URL')
/* upload the sha256 checksums file too */
shaUrl = cmn.uploadArtifact(cmn.pkgFind('sha256'))
/* add URLs to the build description */
cmn.setBuildDesc(
Apk: apkUrl, e2e: e2eUrl, iOS: ipaUrl, App: appUrl, Mac: dmgUrl, Win: exeUrl,
Apk: apkUrl, Apke2e: apke2eUrl,
iOS: ipaUrl, iOSe2e: iose2eUrl,
App: appUrl, Mac: dmgUrl, Win: exeUrl,
)
/* Create latest.json with newest nightly URLs */
if (btype == 'nightly') {
cmn.updateLatestNightlies(
APK: apkUrl, IOS: ipaUrl, APP: appUrl, MAC: dmgUrl, WIN: exeUrl, SHA: shaUrl
APK: apkUrl, IOS: ipaUrl,
APP: appUrl, MAC: dmgUrl,
WIN: exeUrl, SHA: shaUrl
)
}
} }
@ -105,7 +109,9 @@ pipeline {
steps { script {
if (env.CHANGE_ID != null) {
cmn.githubNotify(
apk: apkUrl, e2e: e2eUrl, ipa: ipaUrl, app: appUrl, dmg: dmgUrl, win: exeUrl,
apk: apkUrl, apke2e: apke2eUrl,
ipa: ipaUrl, iose2e: iose2eUrl,
app: appUrl, dmg: dmgUrl, win: exeUrl,
)
}
} }
@ -130,7 +136,7 @@ pipeline {
stage('Run e2e') {
when { expression { btype == 'nightly' } }
steps { script {
e2eApk = e2e.getBuildVariables().get('SAUCE_URL')
e2eApk = apke2e.getBuildVariables().get('SAUCE_URL')
build(
job: 'end-to-end-tests/status-app-nightly', wait: false,
parameters: [string(name: 'apk', value: "--apk=${e2eApk}")]

View File

@ -1,5 +1,5 @@
pipeline {
agent { label 'fastlane' }
agent { label 'macos' }
options {
timestamps()
@ -7,8 +7,8 @@ pipeline {
timeout(time: 35, unit: 'MINUTES')
/* Limit builds retained */
buildDiscarder(logRotator(
numToKeepStr: '60',
daysToKeepStr: '30',
numToKeepStr: '90',
daysToKeepStr: '60',
artifactNumToKeepStr: '60',
))
}
@ -66,7 +66,14 @@ pipeline {
}
stage('Upload') {
steps {
script { env.DIAWI_URL = mobile.ios.uploadToDiawi() }
script {
switch (cmn.getBuildType()) {
case 'nightly':
env.DIAWI_URL = mobile.ios.uploadToDiawi(); break;
case 'e2e':
env.SAUCE_URL = mobile.ios.uploadToSauceLabs(); break;
}
}
}
}
}

View File

@ -26,9 +26,9 @@ def uploadToPlayStore(type = 'nightly') {
def uploadToSauceLabs() {
def changeId = common.getParentRunEnv('CHANGE_ID')
if (changeId != null) {
env.SAUCE_LABS_APK = "${changeId}.apk"
env.SAUCE_LABS_NAME = "${changeId}.apk"
} else {
env.SAUCE_LABS_APK = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
env.SAUCE_LABS_NAME = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
}
withCredentials([
string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'),
@ -36,11 +36,11 @@ def uploadToSauceLabs() {
]) {
sh 'bundle exec fastlane android saucelabs'
}
return env.SAUCE_LABS_APK
return env.SAUCE_LABS_NAME
}
def uploadToDiawi() {
env.SAUCE_LABS_APK = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
env.SAUCE_LABS_NAME = "im.status.ethereum-e2e-${GIT_COMMIT.take(6)}.apk"
withCredentials([
string(credentialsId: 'diawi-token', variable: 'DIAWI_TOKEN'),
]) {

View File

@ -144,7 +144,7 @@ def githubNotify(Map urls) {
message += "CI BUILD SUCCESSFUL in ${currentBuild.durationString} (${GIT_COMMIT})\n"
message += '| | | | | |\n'
message += '|-|-|-|-|-|\n'
message += "| [Android](${urls.apk})([e2e](${urls.e2e})) | [iOS](${urls.ipa}) |"
message += "| [Android](${urls.apk}) ([e2e](${urls.apke2e})) | [iOS](${urls.ipa}) ([e2e](${urls.iose2e})) |"
if (dmgUrl != null) {
message += " [MacOS](${urls.dmg}) | [AppImage](${urls.app}) | [Windows](${urls.win}) |"
} else {
@ -163,7 +163,13 @@ def githubNotify(Map urls) {
}
def pkgFind(glob) {
return findFiles(glob: "pkg/*${glob}")[0].path
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 setBuildDesc(Map links) {

View File

@ -1,24 +1,23 @@
common = load('ci/common.groovy')
cmn = load('ci/common.groovy')
def plutil(name, value) {
sh "plutil -replace ${name} -string ${value} ios/StatusIm/Info.plist"
}
def compile(type = 'nightly') {
def target = 'nightly'
if (type == 'release') {
target = 'adhoc'
def target
switch (type) {
case 'release': target = 'adhoc'; break;
case 'testflight': target = 'release'; break;
case 'e2e': target = 'e2e'; break;
default: target = 'nightly';
}
if (type == 'testflight') {
target = 'release'
}
/* configure build metadata */
plutil('CFBundleShortVersionString', common.version())
plutil('CFBundleVersion', common.buildNumber())
plutil('CFBundleBuildUrl', currentBuild.absoluteUrl)
/* the dir might not exist */
sh 'mkdir -p status-e2e'
/* build the actual app */
withCredentials([
string(credentialsId: 'SLACK_URL', variable: 'SLACK_URL'),
@ -29,12 +28,16 @@ def compile(type = 'nightly') {
]) {
sh "bundle exec fastlane ios ${target}"
}
if (type != 'testflight') {
def pkg = common.pkgFilename(type, 'ipa')
sh "cp status-adhoc/StatusIm.ipa ${pkg}"
return pkg
/* rename built file for uploads and archivization */
def pkg = ''
if (type == 'e2e') {
pkg = cmn.pkgFilename('e2e', 'app.zip')
sh "cp status-e2e/StatusIm.app.zip ${pkg}"
} else if (type != 'testflight') {
pkg = cmn.pkgFilename(type, 'ipa')
sh "cp status-adhoc/StatusIm.ipa ${pkg}"
}
return ''
return pkg
}
def uploadToDiawi() {
@ -47,4 +50,20 @@ def uploadToDiawi() {
return diawiUrl
}
def uploadToSauceLabs() {
def changeId = cmn.getParentRunEnv('CHANGE_ID')
if (changeId != null) {
env.SAUCE_LABS_NAME = "${changeId}.app.zip"
} else {
env.SAUCE_LABS_NAME = "im.status.ethereum-e2e-${cmn.gitCommit()}.app.zip"
}
withCredentials([
string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'),
string(credentialsId: 'SAUCE_USERNAME', variable: 'SAUCE_USERNAME'),
]) {
sh 'bundle exec fastlane ios saucelabs'
}
return env.SAUCE_LABS_NAME
}
return this

View File

@ -27,7 +27,7 @@ def podUpdate() {
try {
wait(lockFile)
sh "touch ${lockFile}"
sh 'pod update --silent'
sh 'pod update --silent --no-ansi'
} finally {
sh "rm ${lockFile}"
}

View File

@ -27,9 +27,9 @@ end
# uploads `file` to sauce labs (overwrites if there is anoter file from the
# same commit)
def upload_to_saucelabs(file)
username = ENV["SAUCE_USERNAME"]
key = ENV["SAUCE_ACCESS_KEY"]
unique_name = ENV["SAUCE_LABS_APK"]
username = ENV["SAUCE_USERNAME"]
unique_name = ENV["SAUCE_LABS_NAME"]
url = "https://saucelabs.com/rest/v1/storage/" + username + '/' + unique_name + "?overwrite=true"
@ -71,6 +71,50 @@ def build_ios_adhoc
)
end
# builds an ios app with e2e configuration and put it
# to "status-e2e" output folder
def build_ios_e2e
# determine a simulator SDK installed
showsdks_output = sh('xcodebuild', '-showsdks')
simulator_sdk = showsdks_output.scan(/iphonesimulator\d\d?\.\d\d?/).first
match(
type: "adhoc",
force_for_new_devices: true,
readonly: true,
keychain_name: "login.keychain"
)
build_ios_app(
# Creating a build for the iOS Simulator
# 1. https://medium.com/rocket-fuel/fastlane-to-the-simulator-87549b2601b9
sdk: simulator_sdk,
destination: "generic/platform=iOS Simulator",
# 2. fixing compilations issues as stated in https://stackoverflow.com/a/20505258
# it looks like i386 isn't supported by React Native
xcargs: "ARCHS=\"x86_64\" ONLY_ACTIVE_ARCH=NO",
# 3. directory where to up StatusIm.app
derived_data_path: "status-e2e",
output_name: "StatusIm.app",
# -------------------------------------
# Normal stuff
scheme: "StatusIm",
workspace: "ios/StatusIm.xcworkspace",
configuration: "Release",
# Simulator apps can't be archived...
skip_archive: true,
# ...and we don't need an .ipa file for them, because we use .app directly
skip_package_ipa: true
)
zip(
path: "status-e2e/Build/Products/Release-iphonesimulator/StatusIm.app",
output_path: "status-e2e/StatusIm.app.zip",
verbose: false,
)
end
def upload_to_diawi(source)
diawi(
@ -84,7 +128,7 @@ end
platform :ios do
desc "`fastlane ios adhoc` - ad-hoc lane for iOS."
desc "This lane is used PRs, Releases, etc."
desc "This lane is used for PRs, Releases, etc."
desc "It creates an .ipa that can be used by a list of devices, registeded in the App Store Connect."
desc "This .ipa is ready to be distibuted through diawi.com"
lane :adhoc do
@ -92,6 +136,14 @@ platform :ios do
build_ios_adhoc
end
desc "`fastlane ios e2e` - e2e lane for iOS."
desc "This lane is used for SauceLabs end-to-end testing."
desc "It creates an .app that can be used inside of a iPhone simulator."
lane :e2e do
unlock_keychain_if_needed
build_ios_e2e
end
desc "`fastlane ios pr` - makes a new pr build"
desc "This lane builds a new adhoc build and leaves an .ipa that is ad-hoc signed (can be uploaded to diawi)"
lane :pr do
@ -149,6 +201,19 @@ platform :ios do
upload_to_diawi("status-adhoc/StatusIm.ipa")
end
desc "`fastlane ios saucelabs` - upload .app to sauce labs"
desc "also notifies in a GitHub comments"
desc "expects to have an .apk prepared: `android/app/build/outputs/apk/release/app-release.apk`"
desc "expects to have a saucelabs access key as SAUCE_ACCESS_KEY env variable"
desc "expects to have a saucelabs username token as SAUCE_USERNAME env variable"
desc "expects to have a saucelabs destination name as SAUCE_LABS_NAME env variable"
desc "will fails if file isn't there"
lane :saucelabs do
upload_to_saucelabs(
"status-e2e/StatusIm.app.zip"
)
end
desc "This fastlane step cleans up XCode DerivedData folder"
lane :cleanup do
clear_derived_data
@ -204,7 +269,7 @@ platform :android do
desc "expects to have an .apk prepared: `android/app/build/outputs/apk/release/app-release.apk`"
desc "expects to have a saucelabs access key as SAUCE_ACCESS_KEY env variable"
desc "expects to have a saucelabs username token as SAUCE_USERNAME env variable"
desc "expects to have a saucelabs destination name as SAUCE_LABS_APK env variable"
desc "expects to have a saucelabs destination name as SAUCE_LABS_NAME env variable"
desc "will fails if file isn't there"
lane :saucelabs do
upload_to_saucelabs(