mirror of
https://github.com/status-im/status-app.git
synced 2026-08-27 15:11:20 +00:00
343 lines
10 KiB
Groovy
343 lines
10 KiB
Groovy
#!/usr/bin/env groovy
|
|
library 'status-jenkins-lib@v1.9.46'
|
|
|
|
pipeline {
|
|
agent {
|
|
dockerfile {
|
|
label getAgentLabel()
|
|
filename 'tests.Dockerfile'
|
|
dir 'ci'
|
|
args '--network=host ' +
|
|
'--cap-add=SYS_ADMIN ' +
|
|
'--security-opt=apparmor:unconfined ' +
|
|
'--device=/dev/fuse ' +
|
|
'--volume=/var/run/docker.sock:/var/run/docker.sock ' +
|
|
'--volume=/home/jenkins/.squish-license:/home/jenkins/.squish-license ' +
|
|
'--volume=/opt/squish-runner-9.2.2-qt-6.11:/opt/squish-runner-9.2.2-qt-6.11 ' +
|
|
'--user jenkins'
|
|
}
|
|
}
|
|
|
|
parameters {
|
|
string(
|
|
name: 'GIT_REF',
|
|
description: 'Git branch to checkout.',
|
|
defaultValue: 'master'
|
|
)
|
|
string(
|
|
name: 'BUILD_SOURCE',
|
|
description: 'URL to tar.gz file OR path to Jenkins build.',
|
|
defaultValue: getDefaultBuildSource()
|
|
)
|
|
string(
|
|
name: 'TEST_NAME',
|
|
description: 'Paste test name/part of test name to run specific test.',
|
|
defaultValue: ''
|
|
)
|
|
string(
|
|
name: 'TEST_SCOPE_FLAG',
|
|
description: 'Paste a known mark to run tests labeled with this mark',
|
|
defaultValue: getDefaultTestScopeFlag()
|
|
)
|
|
string(
|
|
name: 'TESTRAIL_RUN_NAME',
|
|
description: 'Test run name in Test Rail.',
|
|
defaultValue: ''
|
|
)
|
|
choice(
|
|
name: 'LOG_LEVEL',
|
|
description: 'Log level for pytest.',
|
|
choices: ['INFO', 'DEBUG', 'TRACE', 'WARNING', 'CRITICAL']
|
|
)
|
|
choice(
|
|
name: 'AGENT_NODE',
|
|
description: 'Which Linux worker to run on (leave default for random selection)',
|
|
choices: [
|
|
'',
|
|
'linux-01',
|
|
'linux-02',
|
|
'linux-03'
|
|
],
|
|
)
|
|
}
|
|
|
|
options {
|
|
timestamps()
|
|
/* Prevent Jenkins jobs from running forever */
|
|
timeout(time: 120, unit: 'MINUTES')
|
|
/* manage how many builds we keep */
|
|
buildDiscarder(logRotator(
|
|
numToKeepStr: '50',
|
|
daysToKeepStr: '60',
|
|
artifactNumToKeepStr: '10',
|
|
artifactDaysToKeepStr: '30',
|
|
))
|
|
disableRestartFromStage()
|
|
}
|
|
|
|
environment {
|
|
PLATFORM = 'tests/e2e'
|
|
|
|
SQUISH_DIR = '/opt/squish-runner-9.2.2-qt-6.11'
|
|
PYTHONPATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/lib/python:${PYTHONPATH}"
|
|
LD_LIBRARY_PATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/python3/lib:${LD_LIBRARY_PATH}"
|
|
|
|
/* To stop e2e tests using port 8545 */
|
|
STATUS_RUNTIME_HTTP_API = 'False'
|
|
STATUS_RUNTIME_WS_API = 'False'
|
|
|
|
/* Avoid race conditions with other builds using virtualenv. */
|
|
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
|
|
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
|
|
|
|
/* To store user configuratiin files in temp dir */
|
|
XDG_CONFIG_HOME = "${WORKSPACE_TMP}/config"
|
|
|
|
/* To remove QML cache */
|
|
QML_DISABLE_CACHE = 1
|
|
|
|
TESTRAIL_URL = 'https://ethstatus.testrail.net'
|
|
TESTRAIL_PROJECT_ID = 18
|
|
/* Override QT xcb plugin with linux to avoid errors like:
|
|
* "Could not load the Qt platform plugin "xcb" in "" even though it was found." */
|
|
QT_QPA_PLATFORM = "linuxfb"
|
|
|
|
/* Runtime flag to make testing of the app easier. Helps avoiding banners */
|
|
STATUS_RUNTIME_TEST_MODE = 1
|
|
|
|
/* Logging rules let you enable or disable logging for categories */
|
|
QT_LOGGING_RULES = '*.warning=true'
|
|
|
|
/* Set to a non-zero value to make Qt print out diagnostic information about the each (C++) plugin it tries to load. */
|
|
/* QT_DEBUG_PLUGINS = 0 */
|
|
|
|
/* Use local waku fleet */
|
|
E2E_LOCAL_WAKU_FLEET = '1'
|
|
STATUS_FLEET = 'status-app.test'
|
|
STATUS_FLEET_CONFIG_FILE = "${WORKSPACE}/assets/local-waku-fleets-config.json"
|
|
|
|
/* Hack fix for params not being set in job on first run */
|
|
BUILD_SOURCE = "${params.BUILD_SOURCE}"
|
|
TEST_NAME = "${params.TEST_NAME}"
|
|
TEST_SCOPE_FLAG = "${params.TEST_SCOPE_FLAG}"
|
|
TESTRAIL_RUN_NAME = "${params.TESTRAIL_RUN_NAME}"
|
|
LOG_LEVEL = "${params.LOG_LEVEL}"
|
|
|
|
}
|
|
|
|
stages {
|
|
stage('Cleanup Workspace') {
|
|
steps {
|
|
sh './scripts/clean-git.sh'
|
|
}
|
|
}
|
|
stage('Fetch submodules') {
|
|
steps {
|
|
sh 'git submodule update --init --recursive'
|
|
}
|
|
}
|
|
stage('Prep') {
|
|
steps { script {
|
|
setNewBuildName()
|
|
updateGitHubStatus()
|
|
} }
|
|
}
|
|
|
|
stage('Deps') {
|
|
steps { script { dir('test/e2e') {
|
|
sh "python3 -m venv ${VIRTUAL_ENV}"
|
|
sh 'pip3 install -r requirements.txt'
|
|
} } }
|
|
}
|
|
|
|
stage('Download') {
|
|
when { expression { params.BUILD_SOURCE.startsWith('http') } }
|
|
steps { timeout(5) { script { dir('test/e2e') {
|
|
sh 'mkdir -p ./pkg/'
|
|
setBuildDescFromFile(params.BUILD_SOURCE)
|
|
fileOperations([
|
|
fileDownloadOperation(
|
|
url: params.BUILD_SOURCE,
|
|
targetFileName: 'StatusIm-Desktop.tar.gz',
|
|
targetLocation: './pkg/',
|
|
userName: '',
|
|
password: '',
|
|
)
|
|
])
|
|
} } } }
|
|
}
|
|
|
|
stage('Copy') {
|
|
when { expression { ! params.BUILD_SOURCE.startsWith('http') } }
|
|
steps { timeout(5) { script { dir('test/e2e') {
|
|
copyArtifacts(
|
|
projectName: params.BUILD_SOURCE,
|
|
filter: 'pkg/*-x86_64.tar.gz',
|
|
selector: lastWithArtifacts(),
|
|
target: './'
|
|
)
|
|
setBuildDescFromFile(utils.findFile('pkg/*tar.gz'))
|
|
} } } }
|
|
}
|
|
|
|
stage('Unpack') {
|
|
steps { timeout(5) { script { dir('test/e2e') {
|
|
sh 'mkdir aut'
|
|
sh "tar -zxvf '${utils.findFile('pkg/*tar.gz')}' -C './aut'"
|
|
env.AUT_PATH = utils.findFile('aut/*.AppImage')
|
|
} } } }
|
|
}
|
|
|
|
stage('Test') {
|
|
steps {
|
|
lock(resource: "e2e-linux-${env.NODE_NAME}", quantity: 1) {
|
|
script {
|
|
try {
|
|
timeout(2) {
|
|
/* Clean up any leftover containers from a previous aborted run */
|
|
sh 'docker compose -p e2e-fleet -f docker-compose.waku.yml down --remove-orphans || true'
|
|
sh 'docker compose -p e2e-fleet -f docker-compose.waku.yml up -d'
|
|
sh 'sleep 15'
|
|
}
|
|
timeout(time: getTestStageTimeout()) {
|
|
dir('test/e2e') {
|
|
def flags = []
|
|
if (params.TEST_NAME) { flags.add("-k=${params.TEST_NAME}") }
|
|
if (params.TEST_SCOPE_FLAG) { flags.add(params.TEST_SCOPE_FLAG) }
|
|
if (params.LOG_LEVEL) { flags.addAll(["--log-level=${params.LOG_LEVEL}", "--log-cli-level=${params.LOG_LEVEL}"]) }
|
|
dir ('configs') { sh 'ln -s _local.ci.py _local.py' }
|
|
wrap([
|
|
$class: 'Xvfb',
|
|
autoDisplayName: true,
|
|
parallelBuild: true,
|
|
screen: '2560x1440x24',
|
|
additionalOptions: '-dpi 1'
|
|
]) {
|
|
sh 'fluxbox &'
|
|
withCredentials([
|
|
usernamePassword(
|
|
credentialsId: 'test-rail-api-devops',
|
|
usernameVariable: 'TESTRAIL_USR',
|
|
passwordVariable: 'TESTRAIL_PSW'
|
|
),
|
|
string(credentialsId: 'wallet-test-user-seed', variable: 'WALLET_TEST_USER_SEED')
|
|
]) {
|
|
sh """
|
|
python3 -m pytest -m "not keycard and not benchmark" -v --reruns=1 --timeout=300 ${flags.join(" ")} \
|
|
--disable-warnings \
|
|
--alluredir=./allure-results \
|
|
-o timeout_func_only=true
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
/* Stop local waku fleet */
|
|
sh 'docker compose -p e2e-fleet -f docker-compose.waku.yml down --remove-orphans || true'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
always { script {
|
|
dir('test/e2e') {
|
|
archiveArtifacts('aut/*.*')
|
|
|
|
/* Needed to categorize types of errors and add environment section in allure report. */
|
|
sh 'cp ext/allure_files/categories.json allure-results'
|
|
sh 'cp ext/allure_files/environment.properties allure-results'
|
|
|
|
allure([
|
|
results: [[path: 'allure-results']],
|
|
reportBuildPolicy: 'ALWAYS',
|
|
properties: [],
|
|
jdk: '',
|
|
])
|
|
/* Link for Jenkins Builds GitHub comment. */
|
|
env.PKG_URL = "${env.BUILD_URL}allure/"
|
|
updateGitHubStatus()
|
|
}
|
|
} }
|
|
success { script {
|
|
github.notifyPR(true)
|
|
} }
|
|
failure { script {
|
|
github.notifyPR(false)
|
|
discord.send(
|
|
header: '**Desktop E2E test failure!**',
|
|
cred: 'discord-status-desktop-e2e-webhook',
|
|
)
|
|
} }
|
|
cleanup {
|
|
cleanWs(disableDeferredWipeout: true)
|
|
dir(env.WORKSPACE_TMP) { deleteDir() }
|
|
}
|
|
}
|
|
}
|
|
|
|
def setNewBuildName() {
|
|
if (currentBuild.upstreamBuilds) {
|
|
def parent = utils.parentOrCurrentBuild()
|
|
currentBuild.displayName = parent.getFullDisplayName().minus('status-app » ')
|
|
}
|
|
}
|
|
|
|
def setBuildDescFromFile(fileNameOrPath) {
|
|
def tokens = utils.parseFilename(utils.baseName(fileNameOrPath))
|
|
if (tokens == null) { /* Fallback for regex fail. */
|
|
currentBuild.description = utils.baseName(fileNameOrPath)
|
|
return
|
|
}
|
|
if (tokens.build && tokens.build.startsWith('pr')) {
|
|
currentBuild.displayName = tokens.build.replace(/^pr/, 'PR-')
|
|
}
|
|
currentBuild.description = formatMap([
|
|
Node: NODE_NAME,
|
|
Build: tokens.build,
|
|
Commit: tokens.commit,
|
|
Version: (tokens.tstamp ?: tokens.version),
|
|
])
|
|
}
|
|
|
|
def updateGitHubStatus() {
|
|
/* For PR builds update check status. */
|
|
if (params.BUILD_SOURCE ==~ /.*\/PR-[0-9]+\/?$/) {
|
|
github.statusUpdate(
|
|
context: 'jenkins/prs/tests/e2e-new',
|
|
commit: jenkins.getJobCommitByPath(params.BUILD_SOURCE),
|
|
repo_url: 'https://github.com/status-im/status-app'
|
|
)
|
|
}
|
|
}
|
|
|
|
def formatMap(Map data=[:]) {
|
|
def text = ''
|
|
data.each { key, val -> text += "<b>${key}</b>: ${val}</a><br>\n" }
|
|
return text
|
|
}
|
|
|
|
def getDefaultBuildSource() {
|
|
return ''
|
|
}
|
|
|
|
def getDefaultTestScopeFlag() {
|
|
if (JOB_NAME == "status-app/systems/linux/x86_64/tests-e2e") {
|
|
return ''
|
|
} else {
|
|
return '-m=critical'
|
|
}
|
|
}
|
|
|
|
def getTestStageTimeout() { params.TEST_SCOPE_FLAG == '-m=critical' ? 30 : 120 }
|
|
|
|
def getAgentLabel() {
|
|
if (params.AGENT_NODE && params.AGENT_NODE != '') {
|
|
return "linuxcontainer && ${params.AGENT_NODE}"
|
|
}
|
|
return 'linuxcontainer'
|
|
}
|