|
| 1 | +#!/usr/bin/env groovy |
| 2 | + |
| 3 | + |
| 4 | +pipeline { |
| 5 | + |
| 6 | + agent { |
| 7 | + label 'windows && x86_64 && qt-6.9.0 && go-1.23 && windows-04' |
| 8 | + } |
| 9 | + |
| 10 | + parameters { |
| 11 | + gitParameter( |
| 12 | + name: 'GIT_REF', |
| 13 | + description: 'Git branch to checkout.', |
| 14 | + branchFilter: 'origin/(.*)', |
| 15 | + branch: '', |
| 16 | + defaultValue: 'master', |
| 17 | + quickFilterEnabled: false, |
| 18 | + selectedValue: 'DEFAULT', |
| 19 | + sortMode: 'ASCENDING_SMART', |
| 20 | + tagFilter: '*', |
| 21 | + type: 'PT_BRANCH' |
| 22 | + ) |
| 23 | + string( |
| 24 | + name: 'BUILD_SOURCE', |
| 25 | + description: 'URL to tar.gz file OR path to Jenkins build.', |
| 26 | + defaultValue: getDefaultBuildSource() |
| 27 | + ) |
| 28 | + string( |
| 29 | + name: 'TEST_NAME', |
| 30 | + description: 'Paste test name/part of test name to run specific test.', |
| 31 | + defaultValue: '' |
| 32 | + ) |
| 33 | + string( |
| 34 | + name: 'TEST_SCOPE_FLAG', |
| 35 | + description: 'Paste a known mark to run tests labeled with this mark', |
| 36 | + defaultValue: getDefaultTestScopeFlag() |
| 37 | + ) |
| 38 | + string( |
| 39 | + name: 'TESTRAIL_RUN_NAME', |
| 40 | + description: 'Test run name in Test Rail.', |
| 41 | + defaultValue: '' |
| 42 | + ) |
| 43 | + choice( |
| 44 | + name: 'LOG_LEVEL', |
| 45 | + description: 'Log level for pytest.', |
| 46 | + choices: ['INFO', 'DEBUG', 'TRACE', 'WARNING', 'CRITICAL'] |
| 47 | + ) |
| 48 | + } |
| 49 | + |
| 50 | + options { |
| 51 | + timestamps() |
| 52 | + /* Prevent Jenkins jobs from running forever */ |
| 53 | + timeout(time: 120, unit: 'MINUTES') |
| 54 | + /* manage how many builds we keep */ |
| 55 | + buildDiscarder(logRotator( |
| 56 | + daysToKeepStr: '60', |
| 57 | + numToKeepStr: '50', |
| 58 | + artifactNumToKeepStr: '50', |
| 59 | + )) |
| 60 | + disableRestartFromStage() |
| 61 | + } |
| 62 | + |
| 63 | + environment { |
| 64 | + PLATFORM = 'tests/e2e' |
| 65 | + |
| 66 | + SQUISH_DIR = 'C:/squish-runner-9.0.1-qt-6.9' |
| 67 | + PYTHONPATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/lib/python:${PYTHONPATH}" |
| 68 | + LD_LIBRARY_PATH = "${SQUISH_DIR}/lib:${SQUISH_DIR}/python3/lib:${LD_LIBRARY_PATH}" |
| 69 | + |
| 70 | + /* To stop e2e tests using port 8545 */ |
| 71 | + STATUS_RUNTIME_HTTP_API = 'False' |
| 72 | + STATUS_RUNTIME_WS_API = 'False' |
| 73 | + |
| 74 | + /* Avoid race conditions with other builds using virtualenv. */ |
| 75 | + VIRTUAL_ENV = "${WORKSPACE_TMP}/venv" |
| 76 | + PATH = "${VIRTUAL_ENV}/bin:${PATH}" |
| 77 | + |
| 78 | + /* To store user configuratiin files in temp dir */ |
| 79 | + XDG_CONFIG_HOME = "${WORKSPACE_TMP}/config" |
| 80 | + |
| 81 | + TESTRAIL_URL = 'https://ethstatus.testrail.net' |
| 82 | + TESTRAIL_PROJECT_ID = 18 |
| 83 | + /* Override QT xcb plugin with linux to avoid errors like: |
| 84 | + * "Could not load the Qt platform plugin "xcb" in "" even though it was found." |
| 85 | + QT_QPA_PLATFORM = "linuxfb"*/ |
| 86 | + |
| 87 | + /* Runtime flag to make testing of the app easier. Switched off: unpredictable app behavior under new tests */ |
| 88 | + STATUS_RUNTIME_TEST_MODE = 1 |
| 89 | + |
| 90 | + /* Logging rules let you enable or disable logging for categories */ |
| 91 | + QT_LOGGING_RULES = '*.warning=true' |
| 92 | + |
| 93 | + /* Set to a non-zero value to make Qt print out diagnostic information about the each (C++) plugin it tries to load. */ |
| 94 | + /* QT_DEBUG_PLUGINS = 0 */ |
| 95 | + |
| 96 | + /* Hack fix for params not being set in job on first run */ |
| 97 | + BUILD_SOURCE = "${params.BUILD_SOURCE}" |
| 98 | + TEST_NAME = "${params.TEST_NAME}" |
| 99 | + TEST_SCOPE_FLAG = "${params.TEST_SCOPE_FLAG}" |
| 100 | + TESTRAIL_RUN_NAME = "${params.TESTRAIL_RUN_NAME}" |
| 101 | + LOG_LEVEL = "${params.LOG_LEVEL}" |
| 102 | + |
| 103 | + } |
| 104 | + |
| 105 | + stages { |
| 106 | + stage('Cleanup Workspace') { |
| 107 | + steps { |
| 108 | + sh './scripts/clean-git.sh' |
| 109 | + } |
| 110 | + } |
| 111 | + stage('Prep') { |
| 112 | + steps { script { |
| 113 | + setNewBuildName() |
| 114 | + updateGitHubStatus() |
| 115 | + } } |
| 116 | + } |
| 117 | + |
| 118 | + stage('Deps') { |
| 119 | + steps { script { dir('test/e2e') { |
| 120 | + sh "python3 -m venv ${VIRTUAL_ENV}" |
| 121 | + sh 'pip3 install -r requirements.txt' |
| 122 | + } } } |
| 123 | + } |
| 124 | + |
| 125 | + stage('Download') { |
| 126 | + when { expression { params.BUILD_SOURCE.startsWith('http') } } |
| 127 | + steps { timeout(5) { script { dir('test/e2e') { |
| 128 | + sh 'mkdir -p ./pkg/' |
| 129 | + setBuildDescFromFile(params.BUILD_SOURCE) |
| 130 | + fileOperations([ |
| 131 | + fileDownloadOperation( |
| 132 | + url: params.BUILD_SOURCE, |
| 133 | + targetFileName: 'StatusIm-Desktop.tar.gz', |
| 134 | + targetLocation: './pkg/', |
| 135 | + userName: '', |
| 136 | + password: '', |
| 137 | + ) |
| 138 | + ]) |
| 139 | + } } } } |
| 140 | + } |
| 141 | + |
| 142 | + stage('Copy') { |
| 143 | + when { expression { ! params.BUILD_SOURCE.startsWith('http') } } |
| 144 | + steps { timeout(5) { script { dir('test/e2e') { |
| 145 | + copyArtifacts( |
| 146 | + projectName: params.BUILD_SOURCE, |
| 147 | + filter: 'pkg/*-x86_64.tar.gz', |
| 148 | + selector: lastWithArtifacts(), |
| 149 | + target: './' |
| 150 | + ) |
| 151 | + setBuildDescFromFile(utils.findFile('pkg/*tar.gz')) |
| 152 | + } } } } |
| 153 | + } |
| 154 | + |
| 155 | + stage('Unpack') { |
| 156 | + steps { timeout(5) { script { dir('test/e2e') { |
| 157 | + sh 'mkdir aut' |
| 158 | + sh "tar -zxvf '${utils.findFile('pkg/*tar.gz')}' -C './aut'" |
| 159 | + env.AUT_PATH = utils.findFile('aut/*.AppImage') |
| 160 | + } } } } |
| 161 | + } |
| 162 | + |
| 163 | + stage('Test') { |
| 164 | + steps { timeout(getTestStageTimeout()) { script { dir('test/e2e') { |
| 165 | + def flags = [] |
| 166 | + if (params.TEST_NAME) { flags.add("-k=${params.TEST_NAME}") } |
| 167 | + if (params.TEST_SCOPE_FLAG) { flags.add(params.TEST_SCOPE_FLAG) } |
| 168 | + if (params.LOG_LEVEL) { flags.addAll(["--log-level=${params.LOG_LEVEL}", "--log-cli-level=${params.LOG_LEVEL}"]) } |
| 169 | + dir ('configs') { sh 'ln -s _local.ci.py _local.py' } |
| 170 | + wrap([ |
| 171 | + $class: 'Xvfb', |
| 172 | + autoDisplayName: true, |
| 173 | + parallelBuild: true, |
| 174 | + screen: '2560x1440x24', |
| 175 | + additionalOptions: '-dpi 1' |
| 176 | + ]) { |
| 177 | + sh 'fluxbox &' |
| 178 | + withCredentials([ |
| 179 | + usernamePassword( |
| 180 | + credentialsId: 'test-rail-api-devops', |
| 181 | + usernameVariable: 'TESTRAIL_USR', |
| 182 | + passwordVariable: 'TESTRAIL_PSW' |
| 183 | + ), |
| 184 | + string(credentialsId: 'wallet-test-user-seed', variable: 'WALLET_TEST_USER_SEED') |
| 185 | + ]) { |
| 186 | + /* Keep the --reruns flag first, or it won't work */ |
| 187 | + sh """ |
| 188 | + python3 -m pytest -m "not keycard" -v --reruns=1 --timeout=300 ${flags.join(" ")} \ |
| 189 | + --disable-warnings \ |
| 190 | + --alluredir=./allure-results \ |
| 191 | + -o timeout_func_only=true |
| 192 | + """ |
| 193 | + } |
| 194 | + } |
| 195 | + } } } } |
| 196 | + } |
| 197 | + } |
| 198 | + |
| 199 | + post { |
| 200 | + always { script { dir('test/e2e') { |
| 201 | + archiveArtifacts('aut/*.log') |
| 202 | + |
| 203 | + /* Needed to categorize types of errors and add environment section in allure report. */ |
| 204 | + sh 'cp ext/allure_files/categories.json allure-results' |
| 205 | + sh 'cp ext/allure_files/environment.properties allure-results' |
| 206 | + |
| 207 | + allure([ |
| 208 | + results: [[path: 'allure-results']], |
| 209 | + reportBuildPolicy: 'ALWAYS', |
| 210 | + properties: [], |
| 211 | + jdk: '', |
| 212 | + ]) |
| 213 | + /* Link for Jenkins Builds GitHub comment. */ |
| 214 | + env.PKG_URL = "${env.BUILD_URL}allure/" |
| 215 | + updateGitHubStatus() |
| 216 | + } } } |
| 217 | + success { script { |
| 218 | + github.notifyPR(true) |
| 219 | + } } |
| 220 | + failure { script { |
| 221 | + github.notifyPR(false) |
| 222 | + discord.send( |
| 223 | + header: '**Desktop E2E test failure!**', |
| 224 | + cred: 'discord-status-desktop-e2e-webhook', |
| 225 | + ) |
| 226 | + } } |
| 227 | + cleanup { cleanWs(disableDeferredWipeout: true) } |
| 228 | + } |
| 229 | +} |
| 230 | + |
| 231 | +def setNewBuildName() { |
| 232 | + if (currentBuild.upstreamBuilds) { |
| 233 | + def parent = utils.parentOrCurrentBuild() |
| 234 | + currentBuild.displayName = parent.getFullDisplayName().minus('status-desktop » ') |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +def setBuildDescFromFile(fileNameOrPath) { |
| 239 | + def tokens = utils.parseFilename(utils.baseName(fileNameOrPath)) |
| 240 | + if (tokens == null) { /* Fallback for regex fail. */ |
| 241 | + currentBuild.description = utils.baseName(fileNameOrPath) |
| 242 | + return |
| 243 | + } |
| 244 | + if (tokens.build && tokens.build.startsWith('pr')) { |
| 245 | + currentBuild.displayName = tokens.build.replace(/^pr/, 'PR-') |
| 246 | + } |
| 247 | + currentBuild.description = formatMap([ |
| 248 | + Node: NODE_NAME, |
| 249 | + Build: tokens.build, |
| 250 | + Commit: tokens.commit, |
| 251 | + Version: (tokens.tstamp ?: tokens.version), |
| 252 | + ]) |
| 253 | +} |
| 254 | + |
| 255 | +def updateGitHubStatus() { |
| 256 | + /* For PR builds update check status. */ |
| 257 | + if (params.BUILD_SOURCE ==~ /.*\/PR-[0-9]+\/?$/) { |
| 258 | + github.statusUpdate( |
| 259 | + context: 'jenkins/prs/tests/e2e-new.windows', |
| 260 | + commit: jenkins.getJobCommitByPath(params.BUILD_SOURCE), |
| 261 | + repo_url: 'https://github.com/status-im/status-desktop' |
| 262 | + ) |
| 263 | + } |
| 264 | +} |
| 265 | + |
| 266 | +def formatMap(Map data=[:]) { |
| 267 | + def text = '' |
| 268 | + data.each { key, val -> text += "<b>${key}</b>: ${val}</a><br>\n" } |
| 269 | + return text |
| 270 | +} |
| 271 | + |
| 272 | +def getDefaultBuildSource() { |
| 273 | + return '' |
| 274 | +} |
| 275 | + |
| 276 | +def getDefaultTestScopeFlag() { |
| 277 | + if (JOB_NAME == "status-desktop/systems/windows/x86_64/tests-e2e") { |
| 278 | + return '' |
| 279 | + } else { |
| 280 | + return '-m=critical' |
| 281 | + } |
| 282 | +} |
| 283 | + |
| 284 | +def getTestStageTimeout() { params.TEST_SCOPE_FLAG == '-m=critical' ? 30 : 120 } |
0 commit comments