pipeline { agent { label 'jjb' } options { timestamps() ansiColor('xterm') } stages { stage('Prepare Metadata') { steps { script { def bddRefsRaw = (params.BDD_REFS ?: '').trim() def bddRefs = bddRefsRaw ? bddRefsRaw.split(',').collect { it.trim() }.findAll { it }.unique() : [] if (bddRefs.isEmpty() && (params.BDD_REPO_REF ?: '').trim()) { bddRefs = [params.BDD_REPO_REF.trim()] } if (bddRefs.isEmpty()) { bddRefs = ['main', 'homework_2'] } env.BDD_REFS_EFFECTIVE = bddRefs.join(',') wrap([$class: 'BuildUser']) { env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system' env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system' } currentBuild.displayName = "#${env.BUILD_NUMBER} runner" currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; bddRefs=${env.BDD_REFS_EFFECTIVE}; qaRef=${params.QA_REPO_REF}; mobileRef=${params.MOBILE_REPO_REF}" } } } stage('Run Jobs In Parallel') { steps { script { def fanout = [:] def bddRefs = env.BDD_REFS_EFFECTIVE .split(',') .collect { it.trim() } .findAll { it } if (bddRefs.isEmpty()) { bddRefs = ['main', 'homework_2'] } bddRefs.each { ref -> def refValue = ref def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_') def runFile = "downstream-web-bdd-${refKey}.txt" fanout["web-bdd-${refKey}"] = { def run = build job: 'qa-web-bdd-tests', wait: true, propagate: false, parameters: [ string(name: 'BDD_REPO_URL', value: params.BDD_REPO_URL), string(name: 'BDD_REPO_REF', value: refValue), string(name: 'BROWSER', value: params.BROWSER), string(name: 'BASE_URL', value: params.BASE_URL), string(name: 'SELENOID_URL', value: params.SELENOID_URL), string(name: 'HEADLESS', value: params.HEADLESS) ] writeFile file: runFile, text: "${run.number}|${run.result}\n" } } fanout['selenium'] = { def run = build job: 'qa-selenium-tests', wait: true, propagate: false, parameters: [ string(name: 'QA_REPO_URL', value: params.QA_REPO_URL), string(name: 'QA_REPO_REF', value: params.QA_REPO_REF), string(name: 'BROWSER', value: params.BROWSER), string(name: 'BASE_URL', value: params.BASE_URL), string(name: 'EXECUTION_MODE', value: params.EXECUTION_MODE), string(name: 'SELENOID_URL', value: params.SELENOID_URL), string(name: 'HEADLESS', value: params.HEADLESS) ] writeFile file: 'downstream-selenium.txt', text: "${run.number}|${run.result}\n" } fanout['mobile'] = { def run = build job: 'qa-mobile-appium-tests', wait: true, propagate: false, parameters: [ string(name: 'MOBILE_REPO_URL', value: params.MOBILE_REPO_URL), string(name: 'MOBILE_REPO_REF', value: params.MOBILE_REPO_REF), string(name: 'MOBILE_MAX_EMULATORS', value: params.MOBILE_MAX_EMULATORS), string(name: 'JUNIT_PARALLELISM', value: params.JUNIT_PARALLELISM), string(name: 'APP_URL', value: params.APP_URL), string(name: 'DB_URL', value: params.DB_URL), string(name: 'DB_USER', value: params.DB_USER), string(name: 'DB_PASSWORD', value: params.DB_PASSWORD), string(name: 'WISHLISTS_USERNAME', value: params.WISHLISTS_USERNAME), string(name: 'WISHLISTS_PASSWORD', value: params.WISHLISTS_PASSWORD), string(name: 'GIFTS_USERNAME', value: params.GIFTS_USERNAME), string(name: 'GIFTS_PASSWORD', value: params.GIFTS_PASSWORD), string(name: 'RESERVATION_USERNAME', value: params.RESERVATION_USERNAME), string(name: 'RESERVATION_PASSWORD', value: params.RESERVATION_PASSWORD), string(name: 'RESERVATION_OWNER', value: params.RESERVATION_OWNER) ] writeFile file: 'downstream-mobile.txt', text: "${run.number}|${run.result}\n" } fanout['api'] = { def run = build job: 'qa-api-citrus-tests', wait: true, propagate: false, parameters: [ string(name: 'QA_REPO_URL', value: params.QA_REPO_URL), string(name: 'QA_REPO_REF', value: params.QA_REPO_REF) ] writeFile file: 'downstream-api.txt', text: "${run.number}|${run.result}\n" } fanout['api-contract'] = { def run = build job: 'qa-api-contract-tests', wait: true, propagate: false writeFile file: 'downstream-api-contract.txt', text: "${run.number}|${run.result}\n" } fanout['api-rest'] = { def run = build job: 'qa-api-rest-tests', wait: true, propagate: false, parameters: [ string(name: 'API_REST_REPO_URL', value: params.API_REST_REPO_URL), string(name: 'API_REST_REPO_REF', value: params.API_REST_REPO_REF) ] writeFile file: 'downstream-api-rest.txt', text: "${run.number}|${run.result}\n" } fanout['extra'] = { def run = build job: 'qa-maven-extra-tests', wait: true, propagate: false, parameters: [ string(name: 'EXTRA_REPO_URL', value: params.EXTRA_REPO_URL), string(name: 'EXTRA_REPO_REF', value: params.EXTRA_REPO_REF) ] writeFile file: 'downstream-extra.txt', text: "${run.number}|${run.result}\n" } fanout['playwright'] = { def run = build job: 'qa-playwright-tests', wait: true, propagate: false, parameters: [ string(name: 'PLAYWRIGHT_REPO_URL', value: params.PLAYWRIGHT_REPO_URL), string(name: 'PLAYWRIGHT_REPO_REF', value: params.PLAYWRIGHT_REPO_REF), string(name: 'PLAYWRIGHT_BROWSER', value: params.PLAYWRIGHT_BROWSER), string(name: 'PLAYWRIGHT_HEADLESS', value: params.PLAYWRIGHT_HEADLESS), string(name: 'PLAYWRIGHT_BASE_URL', value: params.BASE_URL), string(name: 'PLAYWRIGHT_DOCKER_IMAGE', value: params.PLAYWRIGHT_DOCKER_IMAGE) ] writeFile file: 'downstream-playwright.txt', text: "${run.number}|${run.result}\n" } parallel fanout def parseRun = { String fileName -> if (!fileExists(fileName)) { return [number: 'n/a', result: 'NOT_BUILT'] } def parts = readFile(fileName).trim().tokenize('|') return [ number: parts ? parts[0] : 'n/a', result: parts.size() > 1 ? parts[1] : 'UNKNOWN' ] } def bddRuns = [:] bddRefs.each { ref -> def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_') bddRuns[ref] = parseRun("downstream-web-bdd-${refKey}.txt") } def seleniumRun = parseRun('downstream-selenium.txt') def mobileRun = parseRun('downstream-mobile.txt') def apiRun = parseRun('downstream-api.txt') def apiContractRun = parseRun('downstream-api-contract.txt') def apiRestRun = parseRun('downstream-api-rest.txt') def extraRun = parseRun('downstream-extra.txt') def playwrightRun = parseRun('downstream-playwright.txt') def lines = [] lines << "job=${env.JOB_NAME}" lines << "build=${env.BUILD_NUMBER}" lines << "trigger_user=${env.RUN_TRIGGER_USER}" lines << "trigger_name=${env.RUN_TRIGGER_NAME}" lines << "bdd_repo_url=${params.BDD_REPO_URL}" lines << "bdd_refs=${bddRefs.join(',')}" lines << "qa_repo_url=${params.QA_REPO_URL}" lines << "qa_repo_ref=${params.QA_REPO_REF}" lines << "mobile_repo_url=${params.MOBILE_REPO_URL}" lines << "mobile_repo_ref=${params.MOBILE_REPO_REF}" lines << "api_rest_repo_url=${params.API_REST_REPO_URL}" lines << "api_rest_repo_ref=${params.API_REST_REPO_REF}" lines << "extra_repo_url=${params.EXTRA_REPO_URL}" lines << "extra_repo_ref=${params.EXTRA_REPO_REF}" lines << "playwright_repo_url=${params.PLAYWRIGHT_REPO_URL}" lines << "playwright_repo_ref=${params.PLAYWRIGHT_REPO_REF}" lines << "playwright_docker_image=${params.PLAYWRIGHT_DOCKER_IMAGE}" bddRefs.each { ref -> def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_') def run = bddRuns[ref] lines << "web_bdd_${refKey}_build=${run.number}" lines << "web_bdd_${refKey}_result=${run.result}" lines << "web_bdd_${refKey}_link=/job/qa-web-bdd-tests/${run.number}/" lines << "web_bdd_${refKey}_link_abs=${env.JENKINS_URL}job/qa-web-bdd-tests/${run.number}/" } def primaryBddRef = bddRefs[0] def primaryBddRun = bddRuns[primaryBddRef] lines << "web_bdd_build=${primaryBddRun.number}" lines << "web_bdd_result=${primaryBddRun.result}" lines << "selenium_build=${seleniumRun.number}" lines << "selenium_result=${seleniumRun.result}" lines << "mobile_build=${mobileRun.number}" lines << "mobile_result=${mobileRun.result}" lines << "api_build=${apiRun.number}" lines << "api_result=${apiRun.result}" lines << "api_contract_build=${apiContractRun.number}" lines << "api_contract_result=${apiContractRun.result}" lines << "api_rest_build=${apiRestRun.number}" lines << "api_rest_result=${apiRestRun.result}" lines << "extra_build=${extraRun.number}" lines << "extra_result=${extraRun.result}" lines << "playwright_build=${playwrightRun.number}" lines << "playwright_result=${playwrightRun.result}" lines << "web_bdd_link=/job/qa-web-bdd-tests/${primaryBddRun.number}/" lines << "selenium_link=/job/qa-selenium-tests/${seleniumRun.number}/" lines << "mobile_link=/job/qa-mobile-appium-tests/${mobileRun.number}/" lines << "api_link=/job/qa-api-citrus-tests/${apiRun.number}/" lines << "api_contract_link=/job/qa-api-contract-tests/${apiContractRun.number}/" lines << "api_rest_link=/job/qa-api-rest-tests/${apiRestRun.number}/" lines << "extra_link=/job/qa-maven-extra-tests/${extraRun.number}/" lines << "playwright_link=/job/qa-playwright-tests/${playwrightRun.number}/" lines << "web_bdd_link_abs=${env.JENKINS_URL}job/qa-web-bdd-tests/${primaryBddRun.number}/" lines << "selenium_link_abs=${env.JENKINS_URL}job/qa-selenium-tests/${seleniumRun.number}/" lines << "mobile_link_abs=${env.JENKINS_URL}job/qa-mobile-appium-tests/${mobileRun.number}/" lines << "api_link_abs=${env.JENKINS_URL}job/qa-api-citrus-tests/${apiRun.number}/" lines << "api_contract_link_abs=${env.JENKINS_URL}job/qa-api-contract-tests/${apiContractRun.number}/" lines << "api_rest_link_abs=${env.JENKINS_URL}job/qa-api-rest-tests/${apiRestRun.number}/" lines << "extra_link_abs=${env.JENKINS_URL}job/qa-maven-extra-tests/${extraRun.number}/" lines << "playwright_link_abs=${env.JENKINS_URL}job/qa-playwright-tests/${playwrightRun.number}/" writeFile file: 'runner-summary.txt', text: lines.join('\n') + '\n' archiveArtifacts allowEmptyArchive: false, artifacts: 'runner-summary.txt' def criticalRuns = [mobileRun, apiRun, apiContractRun, apiRestRun, extraRun] def optionalRuns = [] optionalRuns << seleniumRun optionalRuns.addAll(bddRuns.values()) optionalRuns << playwrightRun def criticalFailed = criticalRuns.any { it.result != 'SUCCESS' } def optionalFailed = optionalRuns.any { it.result != 'SUCCESS' } if (criticalFailed) { error("One or more critical downstream QA jobs failed. See runner-summary.txt for build links and statuses.") } if (optionalFailed) { currentBuild.result = 'UNSTABLE' echo "Optional downstream jobs have failures (web-bdd refs/playwright). See runner-summary.txt for details." } } } } } }