Skip to content

Commit

Permalink
Reorganize build/release pipelines
Browse files Browse the repository at this point in the history
Close #6830
Close #7985
Close #7986

Co-authored-by: paw <paw-hub@users.noreply.github.com>
Co-authored-by: ivk <ivk@tutao.de>
  • Loading branch information
3 people committed Nov 29, 2024
1 parent 5882e38 commit f571735
Show file tree
Hide file tree
Showing 14 changed files with 1,222 additions and 527 deletions.
39 changes: 25 additions & 14 deletions app-ios/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
default_platform(:ios)

platform :ios do
desc "Push a new prod release to AppStore"
lane :appstore_prod do |options|
desc "Build a new Mail prod release with AppStore configuration"
lane :build_mail_prod do |options|
match(
app_identifier: ["de.tutao.tutanota", "de.tutao.tutanota.TutanotaShareExtension", "de.tutao.tutanota.TutanotaNotificationExtension"],
type: "appstore",
Expand All @@ -37,18 +37,23 @@ platform :ios do
include_symbols: true,
verbose: true
)
if options[:submit]
upload_to_app_store(
skip_screenshots: true,
submit_for_review: false,
precheck_include_in_app_purchases: false,
# must use force as long as we don't automatically create html previews
force: true,
api_key_path: ENV["API_KEY_JSON_FILE_PATH"]
)
end
end

desc "Publish a Mail artifact to AppStore"
lane :publish_mail_prod do |options|
sh 'echo "Uploading mail artifact ' + options[:file] + '"'

upload_to_app_store(
ipa: options[:file],
skip_screenshots: true,
submit_for_review: false,
precheck_include_in_app_purchases: false,
# must use force as long as we don't automatically create html previews
force: true,
api_key_path: ENV["API_KEY_JSON_FILE_PATH"]
)
end

desc "Build a new prod release for ad-hoc"
lane :adhoc_prod do |options|
match(
Expand Down Expand Up @@ -77,7 +82,7 @@ platform :ios do


desc "Push a new staging release to TestFlight"
lane :testflight_staging do
lane :build_mail_staging do
match(
app_identifier: ["de.tutao.tutanota.test", "de.tutao.tutanota.test.TutanotaShareExtension", "de.tutao.tutanota.test.TutanotaNotificationExtension"],
type: "appstore",
Expand All @@ -96,13 +101,19 @@ platform :ios do
output_name: "tutanota-" + get_version_number(target: "tutanota") + "-test",
verbose: true
)
end

desc "Publish Mail staging to TestFlight"
lane :publish_mail_staging do |options|
sh 'echo "Uploading mail staging to TestFlight ' + options[:file] + '"'

upload_to_testflight(
ipa: options[:file],
app_identifier: "de.tutao.tutanota.test",
skip_submission: true,
api_key_path: ENV["API_KEY_JSON_FILE_PATH"]
)
end
end

desc "Build a new staging release for ad-hoc"
lane :adhoc_staging do
Expand Down
22 changes: 11 additions & 11 deletions buildSrc/createReleaseDraft.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,6 @@ if (wasRunFromCli) {
}

async function run({ name, tag, notes, uploadFile, dryRun, toFile }) {
const releaseToken = process.env.GITHUB_TOKEN

if (!releaseToken) {
throw new Error("No GITHUB_TOKEN set!")
}

const octokit = new Octokit({
auth: releaseToken,
userAgent: "tuta-github-release-v0.0.1",
})

notes = renderCompleteNotes({ notes: await fs.promises.readFile(notes, { encoding: "utf8" }), files: uploadFile })

if (toFile) {
Expand All @@ -48,6 +37,17 @@ async function run({ name, tag, notes, uploadFile, dryRun, toFile }) {
} else if (dryRun) {
console.log(`dry run, so not creating draft with release notes\n\n${notes}\nand name ${name}, tag ${tag} \n ${uploadFile}`)
} else {
const releaseToken = process.env.GITHUB_TOKEN

if (!releaseToken) {
throw new Error("No GITHUB_TOKEN set!")
}

const octokit = new Octokit({
auth: releaseToken,
userAgent: "tuta-github-release-v0.0.1",
})

const draftResponse = await createReleaseDraft(octokit, name, tag, notes)

const { upload_url, id } = draftResponse.data
Expand Down
179 changes: 61 additions & 118 deletions ci/Android.Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ pipeline {
PATH = "${env.NODE_PATH}:${env.PATH}:/home/jenkins/emsdk/upstream/bin/:/home/jenkins/emsdk/:/home/jenkins/emsdk/upstream/emscripten"
ANDROID_SDK_ROOT = "/opt/android-sdk-linux"
ANDROID_HOME = "/opt/android-sdk-linux"
GITHUB_RELEASE_PAGE = "https://github.com/tutao/tutanota/releases/tag/tutanota-android-release-${VERSION}"
STAGING_FILE_PATH = "build/app-android/tutanota-app-tutao-releaseTest-${VERSION}.apk"
PROD_FILE_PATH = "build/app-android/tutanota-app-tutao-release-${VERSION}.apk"
}

agent {
Expand All @@ -18,19 +19,32 @@ pipeline {

parameters {
booleanParam(
name: 'RELEASE', defaultValue: false,
description: "Build a test and release version of the app. " +
"Uploads both to Nexus and creates a new release on google play, " +
"which must be manually published from play.google.com/console"
name: 'UPLOAD',
defaultValue: false,
description: "Upload staging/prod to Nexus"
)
booleanParam(
name: 'STAGING',
defaultValue: true
)
booleanParam(
name: 'PROD',
defaultValue: true
)
persistentText(
name: "releaseNotes",
defaultValue: "",
description: "release notes for this build"
)
}

stages {
stage("Checking params") {
steps {
script{
if(!params.STAGING && !params.PROD) {
currentBuild.result = 'ABORTED'
error('No artifacts were selected.')
}
}
echo "Params OKAY"
}
} // stage checking params
stage('Check Github') {
steps {
script {
Expand All @@ -47,15 +61,14 @@ pipeline {
}
}
stage('Build') {
stages {
stage('Testing') {
parallel {
stage('Staging') {
when { expression { return params.STAGING } }
environment {
APK_SIGN_ALIAS = "test.tutao.de"
}
agent {
label 'linux'
}
steps {
echo "Building STAGING ${VERSION}"
sh 'npm ci'
sh 'npm run build-packages'
script {
Expand All @@ -72,18 +85,16 @@ pipeline {
]) {
sh 'node android.js -b releaseTest test'
}
stash includes: "build/app-android/tutanota-app-tutao-releaseTest-${VERSION}.apk", name: 'apk-testing'
}
} // stage testing
stash includes: "${STAGING_FILE_PATH}", name: 'apk-staging'
} // steps
} // stage staging
stage('Production') {
when {
expression { return params.RELEASE }
}
when { expression { return params.PROD } }
environment {
APK_SIGN_ALIAS = "tutao.de"
}
steps {
echo "Building ${VERSION}"
echo "Building PROD ${VERSION}"
sh 'npm ci'
sh 'npm run build-packages'
script {
Expand All @@ -100,112 +111,44 @@ pipeline {
]) {
sh 'node android.js -b release prod'
}
stash includes: "build/app-android/tutanota-app-tutao-release-${VERSION}.apk", name: 'apk-production'
stash includes: "${PROD_FILE_PATH}", name: 'apk-production'
}
} // stage production
}
}
} // stages
} // stage build

stage('Publish') {
when {
expression { return params.RELEASE }
}
stages {
stage('Testing') {
stage('Upload to Nexus') {
when { expression { return params.UPLOAD } }
parallel {
stage('Staging') {
when { expression { return params.STAGING } }
steps {
script {
def util = load "ci/jenkins-lib/util.groovy"
unstash 'apk-testing'

util.publishToNexus(
groupId: "app",
artifactId: "android-test",
version: "${VERSION}",
assetFilePath: "${WORKSPACE}/build/app-android/tutanota-app-tutao-releaseTest-${VERSION}.apk",
fileExtension: 'apk'
)

catchError(stageResult: 'UNSTABLE', buildResult: 'SUCCESS', message: 'Failed to upload android test app to Play Store') {
// This doesn't publish to the main app on play store,
// instead it gets published to the hidden "tutanota-test" app
// this happens because the AppId is set to de.tutao.tutanota.test by the android build
// and play store knows which app to publish just based on the id
androidApkUpload(
googleCredentialsId: 'android-app-publisher-credentials',
apkFilesPattern: "build/app-android/tutanota-app-tutao-releaseTest-${VERSION}.apk",
trackName: 'internal',
rolloutPercentage: '100%',
recentChangeList: [
[
language: "en-US",
text : "see: ${GITHUB_RELEASE_PAGE}"
]
]
) // androidApkUpload
} // catchError

}
unstash 'apk-staging'
uploadToNexus("android-test", STAGING_FILE_PATH)
}
} // stage testing
} // stage staging
stage('Production') {
when { expression { return params.PROD } }
steps {
sh 'npm ci'
unstash 'apk-production'

script {
def filePath = "build/app-android/tutanota-app-tutao-release-${VERSION}.apk"
def util = load "ci/jenkins-lib/util.groovy"

util.publishToNexus(
groupId: "app",
artifactId: "android",
version: "${VERSION}",
assetFilePath: "${WORKSPACE}/${filePath}",
fileExtension: 'apk'
)

androidApkUpload(
googleCredentialsId: 'android-app-publisher-credentials',
apkFilesPattern: "${filePath}",
trackName: 'production',
// Don't publish the app to users directly
// It will require manual intervention at play.google.com/console
rolloutPercentage: '0%',
recentChangeList: [
[
language: "en-US",
text : "see: ${GITHUB_RELEASE_PAGE}"
]
]
)
}
uploadToNexus("android", PROD_FILE_PATH)
}
} // stage production
}
}
stage('Tag and publish release page') {
when {
expression { return params.RELEASE }
}
steps {
// Needed to upload it
unstash 'apk-production'
} // stages
} // stage upload to nexus
} // stages
} // pipeline

script {
def filePath = "build/app-android/tutanota-app-tutao-release-${VERSION}.apk"
def uploadToNexus(String artifactId, String filePath) {
script {
def util = load "ci/jenkins-lib/util.groovy"

writeFile file: "notes.txt", text: params.releaseNotes
catchError(stageResult: 'UNSTABLE', buildResult: 'SUCCESS', message: 'Failed to create github release page for android') {
withCredentials([string(credentialsId: 'github-access-token', variable: 'GITHUB_TOKEN')]) {
sh """node buildSrc/createReleaseDraft.js --name '${VERSION} (Android)' \
--tag 'tutanota-android-release-${VERSION}' \
--uploadFile '${WORKSPACE}/${filePath}' \
--notes notes.txt"""
} // withCredentials
} // catchError
sh "rm notes.txt"
} // script
}
}
util.publishToNexus(
groupId: "app",
artifactId: "${artifactId}",
version: "${VERSION}",
assetFilePath: "${WORKSPACE}/${filePath}",
fileExtension: 'apk'
)
}
}
}
Loading

0 comments on commit f571735

Please sign in to comment.