|
| 1 | +#!/usr/bin/env groovy |
| 2 | + |
| 3 | + |
| 4 | +pipeline { |
| 5 | + |
| 6 | + agent { |
| 7 | + label 'windows && x86_64 && qt-6.9.2 && go-1.24 && windows-e2e' |
| 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}\\bin;${SQUISH_DIR}\\lib\\python;${PYTHONPATH}" |
| 68 | + |
| 69 | + /* To stop e2e tests using port 8545 */ |
| 70 | + STATUS_RUNTIME_HTTP_API = 'False' |
| 71 | + STATUS_RUNTIME_WS_API = 'False' |
| 72 | + |
| 73 | + /* Avoid race conditions with other builds using virtualenv. */ |
| 74 | + VIRTUAL_ENV = "${WORKSPACE_TMP}\\venv" |
| 75 | + PATH = "${VIRTUAL_ENV}\\bin;C:\\Qt\\6.9.2\\msvc2022_64\\bin;${PATH}" |
| 76 | + |
| 77 | + /* To store user configuratiin files in temp dir */ |
| 78 | + XDG_CONFIG_HOME = "${WORKSPACE_TMP}/config" |
| 79 | + |
| 80 | + TESTRAIL_URL = 'https://ethstatus.testrail.net' |
| 81 | + TESTRAIL_PROJECT_ID = 18 |
| 82 | + /* Override QT xcb plugin with linux to avoid errors like: |
| 83 | + * "Could not load the Qt platform plugin "xcb" in "" even though it was found." |
| 84 | + QT_QPA_PLATFORM = "linuxfb"*/ |
| 85 | + |
| 86 | + /* Runtime flag to make testing of the app easier. Switched off: unpredictable app behavior under new tests */ |
| 87 | + STATUS_RUNTIME_TEST_MODE = 1 |
| 88 | + |
| 89 | + /* Logging rules let you enable or disable logging for categories */ |
| 90 | + QT_LOGGING_RULES = '*.warning=true' |
| 91 | + |
| 92 | + /* Set to a non-zero value to make Qt print out diagnostic information about the each (C++) plugin it tries to load. */ |
| 93 | + /* QT_DEBUG_PLUGINS = 0 */ |
| 94 | + |
| 95 | + /* Hack fix for params not being set in job on first run */ |
| 96 | + BUILD_SOURCE = "${params.BUILD_SOURCE}" |
| 97 | + TEST_NAME = "${params.TEST_NAME}" |
| 98 | + TEST_SCOPE_FLAG = "${params.TEST_SCOPE_FLAG}" |
| 99 | + TESTRAIL_RUN_NAME = "${params.TESTRAIL_RUN_NAME}" |
| 100 | + LOG_LEVEL = "${params.LOG_LEVEL}" |
| 101 | + |
| 102 | + } |
| 103 | + |
| 104 | + stages { |
| 105 | + stage('Cleanup Workspace') { |
| 106 | + steps { |
| 107 | + sh './scripts/clean-git.sh' |
| 108 | + } |
| 109 | + } |
| 110 | + stage('Prep') { |
| 111 | + steps { script { |
| 112 | + setNewBuildName() |
| 113 | + updateGitHubStatus() |
| 114 | + } } |
| 115 | + } |
| 116 | + |
| 117 | + stage('Deps') { |
| 118 | + steps { script { dir('test/e2e') { |
| 119 | + bat """ |
| 120 | + python310 -m venv ${VIRTUAL_ENV} |
| 121 | + python310 -m pip install --upgrade pip |
| 122 | + python310 -m pip install -r requirements.txt |
| 123 | + """ |
| 124 | + } } } |
| 125 | + } |
| 126 | + |
| 127 | + stage('Download') { |
| 128 | + when { expression { params.BUILD_SOURCE.startsWith('http') } } |
| 129 | + steps { timeout(5) { script { dir('test/e2e') { |
| 130 | + sh 'mkdir -p ./pkg/' |
| 131 | + setBuildDescFromFile(params.BUILD_SOURCE) |
| 132 | + fileOperations([ |
| 133 | + fileDownloadOperation( |
| 134 | + url: params.BUILD_SOURCE, |
| 135 | + targetFileName: 'StatusIm-Desktop.7z', |
| 136 | + targetLocation: './pkg/', |
| 137 | + userName: '', |
| 138 | + password: '', |
| 139 | + ) |
| 140 | + ]) |
| 141 | + } } } } |
| 142 | + } |
| 143 | + |
| 144 | + stage('Copy') { |
| 145 | + when { expression { ! params.BUILD_SOURCE.startsWith('http') } } |
| 146 | + steps { timeout(5) { script { dir('test/e2e') { |
| 147 | + copyArtifacts( |
| 148 | + projectName: params.BUILD_SOURCE, |
| 149 | + filter: 'pkg/*-x86_64.7z', |
| 150 | + selector: lastWithArtifacts(), |
| 151 | + target: './' |
| 152 | + ) |
| 153 | + setBuildDescFromFile(utils.findFile('pkg/*7z')) |
| 154 | + } } } } |
| 155 | + } |
| 156 | + |
| 157 | + stage('Unpack') { |
| 158 | + steps { timeout(5) { script { dir('test/e2e') { |
| 159 | + sh 'mkdir aut' |
| 160 | + sh "7z x '${utils.findFile('pkg/*.7z')}' -o'./aut'" |
| 161 | + env.AUT_PATH = utils.findFile('aut/Status/bin/Status.exe').replace('\\','/') |
| 162 | + } } } } |
| 163 | + } |
| 164 | + |
| 165 | + stage('Test') { |
| 166 | + steps { |
| 167 | + timeout(time: getTestStageTimeout()) { |
| 168 | + dir('test/e2e') { |
| 169 | + script { |
| 170 | + def flags = [] |
| 171 | + if (params.TEST_NAME) { flags.add("-k=${params.TEST_NAME}") } |
| 172 | + if (params.TEST_SCOPE_FLAG) { flags.add(params.TEST_SCOPE_FLAG) } |
| 173 | + if (params.LOG_LEVEL) { flags.addAll(["--log-level=${params.LOG_LEVEL}", "--log-cli-level=${params.LOG_LEVEL}"]) } |
| 174 | + def flagStr = flags.join(' ') |
| 175 | + |
| 176 | + withCredentials([ |
| 177 | + usernamePassword(credentialsId: 'test-rail-api-devops', usernameVariable: 'TESTRAIL_USR', passwordVariable: 'TESTRAIL_PSW'), |
| 178 | + string(credentialsId: 'wallet-test-user-seed', variable: 'WALLET_TEST_USER_SEED') |
| 179 | + ]) { |
| 180 | + sh""" |
| 181 | + pushd configs |
| 182 | + ln -sf _local.ci.py _local.py || cp _local.ci.py _local.py |
| 183 | + popd |
| 184 | +
|
| 185 | + """ |
| 186 | + bat""" |
| 187 | + python310 -m pytest -m "not keycard" -v --reruns=1 --timeout=300 ${flagStr} --disable-warnings --alluredir=./allure-results -o timeout_func_only=true |
| 188 | + """ |
| 189 | + } |
| 190 | + } |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + post { |
| 198 | + always { script { dir('test/e2e') { |
| 199 | + archiveArtifacts('aut/Status/bin/*.log') |
| 200 | + |
| 201 | + /* Needed to categorize types of errors and add environment section in allure report. */ |
| 202 | + sh 'cp ext/allure_files/categories.json allure-results' |
| 203 | + sh 'cp ext/allure_files/environment.properties allure-results' |
| 204 | + |
| 205 | + allure([ |
| 206 | + results: [[path: 'allure-results']], |
| 207 | + reportBuildPolicy: 'ALWAYS', |
| 208 | + properties: [], |
| 209 | + jdk: '', |
| 210 | + ]) |
| 211 | + /* Link for Jenkins Builds GitHub comment. */ |
| 212 | + env.PKG_URL = "${env.BUILD_URL}allure/" |
| 213 | + updateGitHubStatus() |
| 214 | + } } } |
| 215 | + success { script { |
| 216 | + github.notifyPR(true) |
| 217 | + } } |
| 218 | + failure { script { |
| 219 | + github.notifyPR(false) |
| 220 | + discord.send( |
| 221 | + header: '**Desktop E2E test failure!**', |
| 222 | + cred: 'discord-status-desktop-e2e-webhook', |
| 223 | + ) |
| 224 | + } } |
| 225 | + cleanup { cleanWs(disableDeferredWipeout: true) } |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | +def setNewBuildName() { |
| 230 | + if (currentBuild.upstreamBuilds) { |
| 231 | + def parent = utils.parentOrCurrentBuild() |
| 232 | + currentBuild.displayName = parent.getFullDisplayName().minus('status-desktop » ') |
| 233 | + } |
| 234 | +} |
| 235 | + |
| 236 | +def setBuildDescFromFile(fileNameOrPath) { |
| 237 | + def tokens = utils.parseFilename(utils.baseName(fileNameOrPath)) |
| 238 | + if (tokens == null) { /* Fallback for regex fail. */ |
| 239 | + currentBuild.description = utils.baseName(fileNameOrPath) |
| 240 | + return |
| 241 | + } |
| 242 | + if (tokens.build && tokens.build.startsWith('pr')) { |
| 243 | + currentBuild.displayName = tokens.build.replace(/^pr/, 'PR-') |
| 244 | + } |
| 245 | + currentBuild.description = formatMap([ |
| 246 | + Node: NODE_NAME, |
| 247 | + Build: tokens.build, |
| 248 | + Commit: tokens.commit, |
| 249 | + Version: (tokens.tstamp ?: tokens.version), |
| 250 | + ]) |
| 251 | +} |
| 252 | + |
| 253 | +def updateGitHubStatus() { |
| 254 | + /* For PR builds update check status. */ |
| 255 | + if (params.BUILD_SOURCE ==~ /.*\/PR-[0-9]+\/?$/) { |
| 256 | + github.statusUpdate( |
| 257 | + context: 'jenkins/prs/tests/e2e-new.windows', |
| 258 | + commit: jenkins.getJobCommitByPath(params.BUILD_SOURCE), |
| 259 | + repo_url: 'https://github.com/status-im/status-desktop' |
| 260 | + ) |
| 261 | + } |
| 262 | +} |
| 263 | + |
| 264 | +def formatMap(Map data=[:]) { |
| 265 | + def text = '' |
| 266 | + data.each { key, val -> text += "<b>${key}</b>: ${val}</a><br>\n" } |
| 267 | + return text |
| 268 | +} |
| 269 | + |
| 270 | +def getDefaultBuildSource() { |
| 271 | + return '' |
| 272 | +} |
| 273 | + |
| 274 | +def getDefaultTestScopeFlag() { |
| 275 | + if (JOB_NAME == "status-desktop/systems/windows/x86_64/tests-e2e") { |
| 276 | + return '' |
| 277 | + } else { |
| 278 | + return '-m=critical' |
| 279 | + } |
| 280 | +} |
| 281 | + |
| 282 | +def getTestStageTimeout() { params.TEST_SCOPE_FLAG == '-m=critical' ? 30 : 120 } |
0 commit comments