fieldnotes/Jenkinsfile

229 lines
7.2 KiB
Groovy

pipeline {
agent {
dockerfile {
filename 'Dockerfile'
dir 'containers/ci'
additionalBuildArgs '--memory-swap -1'
}
}
options {
disableConcurrentBuilds()
}
parameters {
string(name: 'STABLE_BRANCH', defaultValue: 'master', description: 'La branche stable')
string(name: 'LAST_NOT_FAILED_HASH', defaultValue: '', description: 'Référence de la dernière construction réussie')
}
stages {
stage('Initialize project environment') {
steps {
script {
sh 'make init'
}
}
}
stage('Run dependencies audit') {
steps {
script {
try {
sh 'make audit'
} catch(ex) {
currentBuild.result = "UNSTABLE"
}
}
}
}
stage('Generate release') {
environment {
VERSION_CLASSIFIER = versionClassifier()
RELEASE_CHANNEL = releaseChannel()
RELEASE_APP_ID_SUFFIX = releaseAppIdSuffix()
VERSION_CODE_OVERRIDE = versionCodeOverride()
GRADLE_OPTS = '-Xmx2g -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy=in-process -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"'
SENTRY_LOG_LEVEL = 'debug'
}
steps {
script {
withCredentials([
file(credentialsId: 'ANDROID_RELEASE_KEYSTORE', variable: 'ANDROID_RELEASE_KEYSTORE'),
string(credentialsId: 'ANDROID_RELEASE_STOREPASS', variable: 'ANDROID_RELEASE_STOREPASS'),
string(credentialsId: 'ANDROID_RELEASE_KEYPASS', variable: 'ANDROID_RELEASE_KEYPASS'),
string(credentialsId: 'MAPBOX_ACCESS_TOKEN', variable: 'MAPBOX_ACCESS_TOKEN'),
string(credentialsId: 'SENTRY_DSN', variable: 'SENTRY_DSN'),
string(credentialsId: 'SENTRY_AUTH_TOKEN', variable: 'SENTRY_AUTH_TOKEN'),
string(credentialsId: 'SENTRY_PROJECT', variable: 'SENTRY_PROJECT'),
string(credentialsId: 'SENTRY_ORG', variable: 'SENTRY_ORG'),
]) {
sh '''
# Create .env file
cp .env.dist .env
sed -i "s|^SENTRY_DSN=.*|SENTRY_DSN=${SENTRY_DSN}|" .env
sed -i "s|^MAPBOX_ACCESS_TOKEN=.*|MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN}|" .env
sed -i "s|^ENVIRONMENT=.*|ENVIRONMENT=production|" .env
# Create Sentry release properties
SENTRY_PROPERTIES=android/sentry-release.properties
cp -f android/sentry.properties.dist ${SENTRY_PROPERTIES}
sed -i "s|^defaults.org=.*|defaults.org=${SENTRY_ORG}|" ${SENTRY_PROPERTIES}
sed -i "s|^defaults.project=.*|defaults.project=${SENTRY_PROJECT}|" ${SENTRY_PROPERTIES}
sed -i "s|^auth.token=.*|auth.token=${SENTRY_AUTH_TOKEN}|" ${SENTRY_PROPERTIES}
cat android/sentry-release.properties
# Create new keystore and configure gradle to use it
# See containers/ci/configure-gradle-keystore.sh
/usr/local/bin/configure-gradle-keystore
# Increase Gradle HTTP timeout
echo 'systemProp.org.gradle.internal.http.connectionTimeout=120000' >> /root/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.http.socketTimeout=120000' >> /root/.gradle/gradle.properties
'''
retry(3) {
sh 'make android-release'
}
archiveArtifacts artifacts: 'android/app/build/outputs/apk/release/*.apk', fingerprint: true
}
}
}
}
stage('Publish staging release') {
when {
branch "develop"
}
steps {
script {
withCredentials([
string(credentialsId: 'STAGING_SSH_HOST', variable: 'STAGING_SSH_HOST'),
string(credentialsId: 'STAGING_SSH_PORT', variable: 'STAGING_SSH_PORT'),
sshUserPrivateKey(credentialsId: 'STAGING_SSH_USER_PRIVATE_KEY', keyFileVariable: 'STAGING_SSH_PRIVATE_KEY', usernameVariable: 'STAGING_SSH_USERNAME')
]) {
sh '''
scp -i "${STAGING_SSH_PRIVATE_KEY}" \
-o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" \
-P "${STAGING_SSH_PORT}" \
android/app/build/outputs/apk/release/*.apk \
"${STAGING_SSH_USERNAME}@${STAGING_SSH_HOST}:/fdroid/repo"
'''
}
def versionCode = versionCodeOverride()
def versionClassifier = versionClassifier()
def version = "${versionCode} (${versionClassifier})"
def changelog = getChangelogSinceLastNotFailedCommit()
def message = """
Une nouvelle version de Fieldnotes est disponible sur le dépôt F-Droid (staging).
Changelog:
${changelog}
Projet: ${env.GIT_URL}
Branche: ${env.GIT_BRANCH}
Commit: ${env.GIT_COMMIT}
""".stripIndent()
withCredentials([
string(credentialsId: 'DEPLOY_NOTIFICATION_RECIPIENTS', variable: 'DEPLOY_NOTIFICATION_RECIPIENTS'),
]) {
mail (
to: "${DEPLOY_NOTIFICATION_RECIPIENTS}",
subject: "[Pyxis/Fieldnotes][Staging] Nouvelle version: ${version}",
body: message
)
rocketSend (
channel: "#cadoles-pyxis",
emoji: ':cadoles:',
message: "@all\n" + message,
rawMessage: true
)
}
}
}
}
}
post {
always {
script {
if (currentBuild.currentResult != 'SUCCESS') {
emailext (
subject: "${currentBuild.currentResult} - Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: """
Voir les étapes du job: ${env.BUILD_URL}flowGraphTable
Projet: ${env.GIT_URL}
Branche: ${env.GIT_BRANCH}
Commit: ${env.GIT_COMMIT}
""".stripIndent(),
recipientProviders: [developers(), requestor()],
)
}
}
}
}
}
def isStable() {
return env.BRANCH_NAME == params.STABLE_BRANCH
}
def versionClassifier() {
return isStable() ?
'' :
( 'SNAPSHOT+' + sh(script: 'git log -1 --format=%h', returnStdout: true).trim() )
}
def versionCodeOverride() {
return isStable() ?
'' :
env.BUILD_ID
}
def releaseChannel() {
return isStable() ?
'' :
env.GIT_BRANCH
}
def releaseAppIdSuffix() {
return isStable() ?
'' :
'.staging'
}
@NonCPS
def getLastNotFailedCommit() {
if ( "${env.LAST_NOT_FAILED_HASH}" != "") {
return "${env.LAST_NOT_FAILED_HASH}"
}
def lastNotFailedHash = null
def lastNotFailedBuild = currentBuild.rawBuild.getPreviousNotFailedBuild()
if ( lastNotFailedBuild ) {
lastNotFailedHash = commitHashForBuild( lastNotFailedBuild )
}
return lastNotFailedHash
}
@NonCPS
def commitHashForBuild( build ) {
def scmAction = build?.actions.find { action -> action instanceof jenkins.scm.api.SCMRevisionAction }
return scmAction?.revision?.hash
}
def getChangelogSinceLastNotFailedCommit() {
def lastNotFailedCommit = getLastNotFailedCommit()
return sh(
script: "git log --date='short' --format='(%cd) %h: %s - %an' ${lastNotFailedCommit}^..${env.GIT_COMMIT}",
returnStdout: true
).trim().stripIndent()
}