397 lines
13 KiB
Groovy
397 lines
13 KiB
Groovy
pipeline {
|
|
agent {
|
|
kubernetes {
|
|
label 'lesavka-tests-disk'
|
|
defaultContainer 'rust-ci'
|
|
workspaceVolume dynamicPVC(accessModes: 'ReadWriteOnce', requestsSize: '40Gi', storageClassName: 'longhorn')
|
|
yaml """
|
|
apiVersion: v1
|
|
kind: Pod
|
|
spec:
|
|
securityContext:
|
|
fsGroup: 1000
|
|
fsGroupChangePolicy: OnRootMismatch
|
|
nodeSelector:
|
|
kubernetes.io/arch: arm64
|
|
node-role.kubernetes.io/worker: "true"
|
|
affinity:
|
|
nodeAffinity:
|
|
requiredDuringSchedulingIgnoredDuringExecution:
|
|
nodeSelectorTerms:
|
|
- matchExpressions:
|
|
- key: kubernetes.io/hostname
|
|
operator: NotIn
|
|
values:
|
|
- titan-04
|
|
- titan-06
|
|
- titan-11
|
|
- titan-12
|
|
- titan-13
|
|
- titan-14
|
|
- titan-15
|
|
- titan-17
|
|
- titan-18
|
|
- titan-19
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
preference:
|
|
matchExpressions:
|
|
- key: kubernetes.io/hostname
|
|
operator: In
|
|
values:
|
|
- titan-05
|
|
- titan-07
|
|
- titan-08
|
|
- weight: 90
|
|
preference:
|
|
matchExpressions:
|
|
- key: kubernetes.io/hostname
|
|
operator: NotIn
|
|
values:
|
|
- titan-13
|
|
- titan-15
|
|
- titan-17
|
|
- titan-19
|
|
containers:
|
|
- name: rust-ci
|
|
image: registry.bstein.dev/bstein/lesavka-ci@sha256:699ff0a2b4461f8627fcc19353eae0dee49500486eb1819287b5713ad239c13f
|
|
command: ["cat"]
|
|
tty: true
|
|
env:
|
|
- name: CARGO_HOME
|
|
value: /home/jenkins/agent/.cargo-home
|
|
- name: CARGO_TARGET_DIR
|
|
value: /home/jenkins/agent/workspace/lesavka/target
|
|
resources:
|
|
requests:
|
|
cpu: "50m"
|
|
memory: "1024Mi"
|
|
ephemeral-storage: "4Gi"
|
|
limits:
|
|
memory: "6Gi"
|
|
ephemeral-storage: "8Gi"
|
|
volumeMounts:
|
|
- name: workspace-volume
|
|
mountPath: /home/jenkins/agent
|
|
"""
|
|
}
|
|
}
|
|
|
|
options {
|
|
disableConcurrentBuilds()
|
|
disableResume()
|
|
}
|
|
|
|
parameters {
|
|
booleanParam(name: 'PUSH_IMAGES', defaultValue: false, description: 'Push images to registry (enable for release runs)')
|
|
choice(name: 'LESAVKA_CI_PROFILE', choices: ['safe', 'daily', 'lab'], description: 'Safe is the normal non-disruptive gate; daily is intended for scheduled master/main runs; lab enables explicitly configured bare-metal probes.')
|
|
booleanParam(name: 'RUN_DISRUPTIVE_INPUT_TESTS', defaultValue: false, description: 'Run virtual HID tests only on an isolated worker/session; these can emit keyboard/mouse events.')
|
|
booleanParam(name: 'RUN_LAB_HARDWARE_GATES', defaultValue: false, description: 'Run opt-in bare-metal lab gates for Theia/Tethys/RCT probes when the Jenkins worker is prepared for them.')
|
|
booleanParam(name: 'ENFORCE_COVERAGE_GATE', defaultValue: false, description: 'Fail CI when coverage is below the ratchet target; keep off while Lesavka is onboarding to the shared dashboard.')
|
|
string(name: 'QUALITY_GATE_PUSHGATEWAY_URL', defaultValue: 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091', description: 'Pushgateway base URL for quality gate metrics')
|
|
string(name: 'REGISTRY_CREDENTIALS_ID', defaultValue: 'registry-bstein-dev', description: 'Jenkins credentials id for registry.bstein.dev')
|
|
}
|
|
|
|
environment {
|
|
REGISTRY = 'registry.bstein.dev'
|
|
IMAGE_PREFIX = "${REGISTRY}/lesavka"
|
|
CARGO_TERM_COLOR = 'always'
|
|
CARGO_BUILD_JOBS = '2'
|
|
CARGO_INCREMENTAL = '0'
|
|
CARGO_PROFILE_DEV_DEBUG = '0'
|
|
CARGO_PROFILE_TEST_DEBUG = '0'
|
|
CARGO_HOME = '/home/jenkins/agent/.cargo-home'
|
|
CARGO_TARGET_DIR = "${WORKSPACE}/target"
|
|
PATH = "/home/jenkins/agent/.cargo-home/bin:/usr/local/cargo/bin:${PATH}"
|
|
RUSTFLAGS = '-C link-arg=-fuse-ld=lld'
|
|
DOCKER_BUILDKIT = '1'
|
|
LESAVKA_CI_PROFILE = "${params.LESAVKA_CI_PROFILE}"
|
|
LESAVKA_COVERAGE_ENFORCE = "${params.ENFORCE_COVERAGE_GATE}"
|
|
LESAVKA_CI_HEADLESS_GTK = '1'
|
|
GDK_BACKEND = 'x11'
|
|
NO_AT_BRIDGE = '1'
|
|
QUALITY_GATE_PUSHGATEWAY_URL = "${params.QUALITY_GATE_PUSHGATEWAY_URL}"
|
|
}
|
|
|
|
stages {
|
|
stage('Checkout') {
|
|
steps {
|
|
checkout scm
|
|
}
|
|
}
|
|
|
|
stage('Bootstrap CI Toolchain') {
|
|
steps {
|
|
container('rust-ci') {
|
|
sh '''
|
|
set -eu
|
|
git config --global --add safe.directory "$WORKSPACE"
|
|
if [ -z "${DISPLAY:-}" ] && ! command -v xvfb-run >/dev/null 2>&1; then
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb xauth dbus-x11
|
|
rm -rf /var/lib/apt/lists/*
|
|
fi
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Safe Gate (Single Exec)') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE == 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
sh '''
|
|
set -eu
|
|
|
|
git config --global --add safe.directory "$WORKSPACE"
|
|
|
|
overall_rc=0
|
|
run_gate() {
|
|
name="$1"
|
|
shift
|
|
printf '\\n== %s ==\\n' "$name"
|
|
if "$@"; then
|
|
return 0
|
|
else
|
|
rc=$?
|
|
printf 'gate failed: %s (rc=%s)\\n' "$name" "$rc" >&2
|
|
overall_rc=1
|
|
fi
|
|
}
|
|
|
|
export QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}"
|
|
run_gate "style/docs/loc/naming" scripts/ci/hygiene_gate.sh
|
|
run_gate "tests" scripts/ci/test_gate.sh
|
|
|
|
if ! cargo llvm-cov --version >/dev/null 2>&1; then
|
|
if ! cargo install --locked cargo-llvm-cov; then
|
|
overall_rc=1
|
|
fi
|
|
fi
|
|
run_gate "coverage" scripts/ci/quality_gate.sh
|
|
run_gate "performance" scripts/ci/performance_gate.sh
|
|
run_gate "media reliability" scripts/ci/media_reliability_gate.sh
|
|
|
|
if [ "${RUN_DISRUPTIVE_INPUT_TESTS}" = "true" ]; then
|
|
run_gate "input transport" env LESAVKA_ALLOW_DISRUPTIVE_INPUT_TESTS=1 scripts/ci/input_transport_gate.sh
|
|
fi
|
|
|
|
if [ "${RUN_LAB_HARDWARE_GATES}" = "true" ] || [ "${LESAVKA_CI_PROFILE}" = "lab" ]; then
|
|
run_gate "bare-metal lab" env LESAVKA_ALLOW_LAB_HARDWARE_TESTS=1 scripts/ci/baremetal_lab_gate.sh
|
|
fi
|
|
|
|
run_gate "gate glue" scripts/ci/gate_glue_gate.sh
|
|
run_gate "sonarqube" scripts/ci/sonarqube_gate.sh
|
|
run_gate "build dist" scripts/ci/build-dist.sh
|
|
run_gate "supply chain" scripts/ci/supply_chain_gate.sh
|
|
|
|
exit "$overall_rc"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Daily Master Gate') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE == 'daily' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/daily_master_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Style Docs LOC Naming') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/hygiene_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Tests') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/test_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Coverage') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh '''
|
|
set -eu
|
|
if ! cargo llvm-cov --version >/dev/null 2>&1; then
|
|
cargo install --locked cargo-llvm-cov
|
|
fi
|
|
QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/quality_gate.sh
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Performance') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/performance_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Media Reliability') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/media_reliability_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Input Transport (Isolated Opt-In)') {
|
|
when {
|
|
expression { return params.RUN_DISRUPTIVE_INPUT_TESTS && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" LESAVKA_ALLOW_DISRUPTIVE_INPUT_TESTS=1 scripts/ci/input_transport_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Bare-Metal Lab Gates (Opt-In)') {
|
|
when {
|
|
expression { return (params.RUN_LAB_HARDWARE_GATES || params.LESAVKA_CI_PROFILE == 'lab') && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" LESAVKA_ALLOW_LAB_HARDWARE_TESTS=1 scripts/ci/baremetal_lab_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Gate Glue') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/gate_glue_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('SonarQube') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/sonarqube_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build Dist') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'scripts/ci/build-dist.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Supply Chain Artifact Security') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.LESAVKA_CI_PROFILE != 'safe' }
|
|
}
|
|
steps {
|
|
container('rust-ci') {
|
|
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
|
sh 'QUALITY_GATE_PUSHGATEWAY_URL="${QUALITY_GATE_PUSHGATEWAY_URL}" scripts/ci/supply_chain_gate.sh'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Docker Login') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.PUSH_IMAGES }
|
|
}
|
|
steps {
|
|
withCredentials([
|
|
usernamePassword(
|
|
credentialsId: params.REGISTRY_CREDENTIALS_ID,
|
|
usernameVariable: 'REGISTRY_USER',
|
|
passwordVariable: 'REGISTRY_PASS'
|
|
)
|
|
]) {
|
|
sh 'echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin'
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build Images') {
|
|
when {
|
|
expression { return params.LESAVKA_CI_PROFILE != 'daily' && params.PUSH_IMAGES }
|
|
}
|
|
steps {
|
|
sh 'PUSH_IMAGES=${PUSH_IMAGES} scripts/ci/build-images.sh'
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
always {
|
|
script {
|
|
try {
|
|
archiveArtifacts artifacts: 'dist/**,target/test-gate/**,target/quality-gate/**,target/hygiene-gate/**,target/daily-master-gate/**,target/performance-gate/**,target/media-reliability-gate/**,target/input-transport-gate/**,target/baremetal-lab-gate/**,target/video-downstream-gate/**,target/gate-glue-gate/**,target/sonarqube-gate/**,target/supply-chain-gate/**', fingerprint: true, allowEmptyArchive: true
|
|
} catch (Throwable err) {
|
|
echo "archive step unavailable: ${err.class.simpleName}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|