def suites = ['threadx', 'smp'] def presets = ['Debug32', 'Release32'] def simulatorImageLatest = 'git.minres.com/here/here-vp:latest' def threadxTestSimulator = '/usr/local/bin/riscv-vp' def runOneRegression(String suite, String preset, String simulatorPath) { def suiteDir = "test/${suite}" def buildDir = "../../build/${preset}/test/${suite}" stage("${suite}-${preset}") { dir(suiteDir) { sh """ set -eu test -n "${simulatorPath}" test -x "${simulatorPath}" cmake --fresh --preset "${preset}" \\ -DTHREADX_TEST_SIMULATOR="${simulatorPath}" cmake --build --preset "${preset}" --parallel "\$(nproc)" ctest \\ --test-dir "${buildDir}" \\ --output-on-failure \\ --output-junit "${buildDir}/ctest-results.xml" \\ --parallel "4" """ } } } def resolveImageCommit(String image) { withCredentials([usernamePassword( credentialsId: 'gitea-jenkins', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASS' )]) { return sh( script: """ set -eu echo "\$REGISTRY_PASS" | docker login "git.minres.com" -u "\$REGISTRY_USER" --password-stdin >/dev/null docker pull "${image}" >/dev/null docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}" """, returnStdout: true ).trim() } } def runRegressionLane(String image, String simulatorPath, boolean allowFailure, List suiteNames, List presetNames) { def parallelTasks = [:] for (String suite : suiteNames) { for (String preset : presetNames) { def suiteName = suite def presetName = preset def taskName = "${suiteName}-${presetName}" parallelTasks[taskName] = { if (allowFailure) { catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') { runOneRegression(suiteName, presetName, simulatorPath) } } else { runOneRegression(suiteName, presetName, simulatorPath) } } } } docker.image(image).inside { sh ''' set -eu cmake --version ctest --version ''' parallel parallelTasks } } properties([ parameters([ string( name: 'SIMULATOR_IMAGE_PINNED', defaultValue: 'git.minres.com/here/here-vp:4fcc192', description: 'Version-pinned Docker image used for the blocking regression lane' ) ]) ]) def canaryShouldRun = true node { timestamps { try { stage('Checkout') { checkout scm sh ''' git submodule sync --recursive git submodule update --init --recursive ''' } stage('Check Canary Divergence') { def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED) def latestCommit = resolveImageCommit(simulatorImageLatest) if (!pinnedCommit) { error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}" } if (!latestCommit) { error "Missing git-commit label on ${simulatorImageLatest}" } canaryShouldRun = (pinnedCommit != latestCommit) if (canaryShouldRun) { echo "Canary enabled: ${simulatorImageLatest} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})" } else { echo "Canary skipped: ${simulatorImageLatest} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}" } } stage('Stable Regression') { runRegressionLane(params.SIMULATOR_IMAGE_PINNED, threadxTestSimulator, false, suites, presets) } stage('Simulator Canary') { if (canaryShouldRun) { runRegressionLane(simulatorImageLatest, threadxTestSimulator, true, suites, presets) } else { echo 'Skipping canary lane because latest and pinned images are identical.' } } } catch (err) { currentBuild.result = 'FAILURE' throw err } finally { junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml' archiveArtifacts artifacts: 'build/*/test/*/Testing/**', allowEmptyArchive: true if (currentBuild.currentResult == 'SUCCESS') { rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} " } else if (currentBuild.currentResult == 'UNSTABLE') { rocketSend ":warning: ThreadX canary regression is unstable on ${simulatorImageLatest}, please check ${env.RUN_DISPLAY_URL} " } else if (currentBuild.currentResult == 'FAILURE') { archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} " emailext recipientProviders: [culprits(), requestor()], subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}", body: """
Build Status: ${currentBuild.currentResult}
Check logs at Build Console Logs or at Overview
""" } } } }