0e59fd5bad
YesChef/pipeline/head There was a failure building this commit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
224 lines
8.9 KiB
Groovy
224 lines
8.9 KiB
Groovy
// Jenkins pipeline for YesChef.
|
|
//
|
|
// Stages:
|
|
// 1. Restore + build the .NET backend solution
|
|
// 2. Run backend unit tests
|
|
// 3. Run backend integration tests against a sidecar Postgres container named `postgres`
|
|
// (Testcontainers also works because the docker socket is mounted)
|
|
// 4. Install, type-check, unit-test, and build the SvelteKit frontend
|
|
// 5. Build the backend and frontend Docker images from their existing Dockerfiles
|
|
//
|
|
// Requires the Jenkins Docker Pipeline plugin, an agent with Docker available, and a
|
|
// workspace user that can talk to the Docker daemon (the host docker socket is mounted
|
|
// into the build containers so Testcontainers can spawn its own DB instances).
|
|
|
|
pipeline {
|
|
agent any
|
|
|
|
options {
|
|
timestamps()
|
|
timeout(time: 45, unit: 'MINUTES')
|
|
buildDiscarder(logRotator(numToKeepStr: '20'))
|
|
disableConcurrentBuilds()
|
|
}
|
|
|
|
environment {
|
|
DOTNET_SDK_IMAGE = 'mcr.microsoft.com/dotnet/sdk:10.0'
|
|
NODE_IMAGE = 'node:22-slim'
|
|
POSTGRES_IMAGE = 'postgres:17'
|
|
|
|
POSTGRES_DB = 'yeschef'
|
|
POSTGRES_USER = 'yeschef'
|
|
POSTGRES_PASSWORD = 'yeschef'
|
|
|
|
// Gitea container registry target. The registry lives on the same host as
|
|
// Gitea itself; the owner is the Gitea user/org that owns the images.
|
|
GITEA_REGISTRY = 'git.therogersfamily.tech'
|
|
GITEA_OWNER = 'josh'
|
|
// Jenkins credential ID for the Gitea push. Type: giteaPersonalAccessToken
|
|
// (the gitea-personal-access-token plugin). The PAT must have at least
|
|
// `write:package` scope. The token is bound as a Secret String at push time
|
|
// and fed to `docker login --password-stdin` using GITEA_OWNER as the
|
|
// username.
|
|
GITEA_CREDENTIALS = 'gitea-ci'
|
|
|
|
BACKEND_IMAGE = "${GITEA_REGISTRY}/${GITEA_OWNER}/yeschef-api"
|
|
FRONTEND_IMAGE = "${GITEA_REGISTRY}/${GITEA_OWNER}/yeschef-web"
|
|
IMAGE_TAG = "${env.BUILD_NUMBER}"
|
|
|
|
// Keep NuGet / npm caches inside the workspace so they survive across
|
|
// docker.inside containers without needing a shared host mount.
|
|
DOTNET_CLI_TELEMETRY_OPTOUT = '1'
|
|
NUGET_PACKAGES = "${env.WORKSPACE}/.nuget"
|
|
npm_config_cache = "${env.WORKSPACE}/.npm"
|
|
}
|
|
|
|
stages {
|
|
stage('Checkout') {
|
|
steps {
|
|
checkout scm
|
|
}
|
|
}
|
|
|
|
stage('Backend: restore & build') {
|
|
agent {
|
|
docker {
|
|
image "${DOTNET_SDK_IMAGE}"
|
|
reuseNode true
|
|
}
|
|
}
|
|
steps {
|
|
sh 'dotnet --info'
|
|
sh 'dotnet restore src/backend/YesChef.slnx'
|
|
sh 'dotnet build src/backend/YesChef.slnx -c Release --no-restore'
|
|
}
|
|
}
|
|
|
|
stage('Backend: unit tests') {
|
|
agent {
|
|
docker {
|
|
image "${DOTNET_SDK_IMAGE}"
|
|
reuseNode true
|
|
}
|
|
}
|
|
steps {
|
|
// global.json lives in src/backend and switches dotnet test to MTP mode,
|
|
// which requires --project (positional project paths are rejected).
|
|
dir('src/backend') {
|
|
sh '''
|
|
dotnet test \
|
|
--project YesChef.Api.UnitTests/YesChef.Api.UnitTests.csproj \
|
|
-c Release --no-build \
|
|
--report-trx --results-directory ../../TestResults/backend-unit
|
|
'''
|
|
}
|
|
}
|
|
post {
|
|
always {
|
|
junit allowEmptyResults: true, testResults: 'TestResults/backend-unit/**/*.trx'
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Backend: integration tests') {
|
|
steps {
|
|
script {
|
|
// Sidecar Postgres reachable from the build container at host `postgres`.
|
|
// Testcontainers can still spin up additional DBs because the host docker
|
|
// socket is mounted into the SDK container below.
|
|
docker.image("${POSTGRES_IMAGE}").withRun(
|
|
"-e POSTGRES_DB=${POSTGRES_DB}" +
|
|
" -e POSTGRES_USER=${POSTGRES_USER}" +
|
|
" -e POSTGRES_PASSWORD=${POSTGRES_PASSWORD}"
|
|
) { pg ->
|
|
|
|
// Wait for the DB to accept connections.
|
|
docker.image("${POSTGRES_IMAGE}").inside("--link ${pg.id}:postgres") {
|
|
sh '''
|
|
for i in $(seq 1 30); do
|
|
if pg_isready -h postgres -U yeschef >/dev/null 2>&1; then
|
|
echo "postgres is ready"
|
|
exit 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
echo "postgres did not become ready in time" >&2
|
|
exit 1
|
|
'''
|
|
}
|
|
|
|
docker.image("${DOTNET_SDK_IMAGE}").inside(
|
|
"--link ${pg.id}:postgres" +
|
|
" -v /var/run/docker.sock:/var/run/docker.sock" +
|
|
" -e ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
|
) {
|
|
dir('src/backend') {
|
|
sh '''
|
|
dotnet test \
|
|
--project YesChef.Api.IntegrationTests/YesChef.Api.IntegrationTests.csproj \
|
|
-c Release \
|
|
--report-trx --results-directory ../../TestResults/backend-integration
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
post {
|
|
always {
|
|
junit allowEmptyResults: true, testResults: 'TestResults/backend-integration/**/*.trx'
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Frontend') {
|
|
agent {
|
|
docker {
|
|
image "${NODE_IMAGE}"
|
|
reuseNode true
|
|
}
|
|
}
|
|
stages {
|
|
stage('Install') {
|
|
steps {
|
|
dir('src/frontend') { sh 'npm ci' }
|
|
}
|
|
}
|
|
stage('Type check') {
|
|
steps {
|
|
dir('src/frontend') { sh 'npm run check' }
|
|
}
|
|
}
|
|
stage('Unit tests') {
|
|
steps {
|
|
dir('src/frontend') { sh 'npm run test:unit' }
|
|
}
|
|
}
|
|
stage('Build') {
|
|
steps {
|
|
dir('src/frontend') { sh 'npm run build' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Docker images') {
|
|
steps {
|
|
script {
|
|
def backendImg = docker.build("${BACKEND_IMAGE}:${IMAGE_TAG}", 'src/backend/YesChef.Api')
|
|
def frontendImg = docker.build("${FRONTEND_IMAGE}:${IMAGE_TAG}", 'src/frontend')
|
|
|
|
backendImg.tag('latest')
|
|
frontendImg.tag('latest')
|
|
|
|
// Gitea PAT credential (giteaPersonalAccessToken) — exposes the
|
|
// token as a Secret String, so bind with `string` and feed it to
|
|
// docker login on stdin. Username for the package registry is the
|
|
// Gitea owner.
|
|
withCredentials([string(credentialsId: "${GITEA_CREDENTIALS}", variable: 'GITEA_TOKEN')]) {
|
|
sh """
|
|
set +x
|
|
echo "\$GITEA_TOKEN" | docker login ${GITEA_REGISTRY} -u ${GITEA_OWNER} --password-stdin
|
|
"""
|
|
try {
|
|
sh "docker push ${BACKEND_IMAGE}:${IMAGE_TAG}"
|
|
sh "docker push ${BACKEND_IMAGE}:latest"
|
|
sh "docker push ${FRONTEND_IMAGE}:${IMAGE_TAG}"
|
|
sh "docker push ${FRONTEND_IMAGE}:latest"
|
|
} finally {
|
|
sh "docker logout ${GITEA_REGISTRY} || true"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
always {
|
|
archiveArtifacts artifacts: 'TestResults/**/*.trx', allowEmptyArchive: true, fingerprint: false
|
|
cleanWs(deleteDirs: true, notFailBuild: true)
|
|
}
|
|
}
|
|
}
|