Files
agentic-mobile-control/Jenkinsfile
T
q792602257 52dae806ef
Tests / Test No test results found
Jenkins打包
2026-07-13 10:42:31 +08:00

127 lines
4.6 KiB
Groovy

// Jenkins pipeline: test the Device Cloud Platform (Python / uv workspace),
// build the deployable Docker image (Cloud API + Host Agent share one image;
// the entrypoint is selected via the Compose `command`), smoke-test the
// container, and optionally push it to a registry.
//
// Requires the Docker Pipeline plugin and a Docker-capable agent.
pipeline {
agent any
options {
timestamps()
disableConcurrentBuilds()
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '20'))
}
parameters {
string(name: 'IMAGE_NAME', defaultValue: 'q792602257/agentic-mobile-control', description: 'Image repository name')
string(name: 'REGISTRY', defaultValue: 'git.jerryyan.net', description: 'Registry host, e.g. registry.example.com. Empty = local build only.')
string(name: 'UV_INDEX_URL', defaultValue: 'https://mirrors.aliyun.com/pypi/simple', description: 'Optional PyPI mirror index URL for uv sync (empty = uv default)')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Skip the uv sync/pytest stage (faster builds when only packaging)')
booleanParam(name: 'PUSH', defaultValue: true, description: 'Push the image to REGISTRY after a successful build')
}
environment {
// Short commit for a traceable, immutable tag alongside :latest.
GIT_SHA = sh(script: 'git rev-parse --short HEAD 2>/dev/null || echo unknown', returnStdout: true).trim()
IMAGE_TAG = "${env.BUILD_NUMBER}-${GIT_SHA}"
FULL_IMAGE = "${params.REGISTRY?.trim() ? params.REGISTRY.trim() + '/' : ''}${params.IMAGE_NAME}"
}
stages {
stage('Checkout') {
steps { checkout scm }
}
// Root, cloud-api, and host-agent suites, excluding tests that need real
// hardware/PostgreSQL/device services (see tests/ pytest marker "integration").
stage('Test') {
when { expression { return !params.SKIP_TESTS } }
agent {
docker {
image 'registry-ghcr.jerryyan.top/astral-sh/uv:python3.14-bookworm-slim'
reuseNode true
// The Docker Pipeline plugin runs the container as the Jenkins host
// user (non-root), so /root isn't writable. Cache under /tmp, which is
// world-writable, and mount the persistent host cache dir there.
args '-v $HOME/.cache/uv:/tmp/uv-cache'
}
}
environment {
// Pin HOME/cache explicitly for the same reason as the cache mount above.
HOME = '/tmp'
UV_CACHE_DIR = '/tmp/uv-cache'
UV_LINK_MODE = 'copy'
UV_INDEX_URL = "${params.UV_INDEX_URL}"
}
steps {
sh 'uv sync --locked --all-packages'
sh '''
uv run pytest tests apps/cloud-api/tests apps/device-host-agent/tests \
-m "not integration" -ra --junit-xml=test-results.xml
'''
}
post {
always {
junit testResults: 'test-results.xml', allowEmptyResults: true
}
}
}
stage('Build image') {
steps {
script {
def img = docker.build("${FULL_IMAGE}:${IMAGE_TAG}", '.')
img.tag('latest')
env.BUILT_IMAGE = "${FULL_IMAGE}:${IMAGE_TAG}"
echo "Built ${env.BUILT_IMAGE} (+ :latest)"
}
}
}
// Confirm both packaged console scripts start up before shipping the image.
// A full boot needs PostgreSQL (cloud-api runs Alembic migrations on
// start), so this checks the entrypoints resolve and import cleanly
// instead of booting the real service.
stage('Smoke test') {
steps {
sh '''
set -eu
docker run --rm "${BUILT_IMAGE}" device-cloud-api --help >/dev/null
docker run --rm "${BUILT_IMAGE}" device-host-agent --help >/dev/null
echo "smoke test OK"
'''
}
}
stage('Push') {
when { expression { return params.PUSH } }
steps {
script {
def registryUrl = params.REGISTRY?.trim() ? "https://${params.REGISTRY.trim()}" : ''
docker.withRegistry(registryUrl, 'gitea-registry') {
def img = docker.image("${FULL_IMAGE}:${IMAGE_TAG}")
img.push()
img.push('latest')
}
echo "Pushed ${FULL_IMAGE}:${IMAGE_TAG} and :latest"
}
}
}
}
post {
always {
// Free local disk: drop this build's tag and any dangling layers.
sh '''
[ -n "${BUILT_IMAGE:-}" ] && docker rmi "${BUILT_IMAGE}" >/dev/null 2>&1 || true
docker image prune -f >/dev/null 2>&1 || true
'''
cleanWs()
}
success { echo "OK: ${env.FULL_IMAGE}:${env.IMAGE_TAG}" }
failure { echo 'Build failed — see stage logs above.' }
}
}