ci: add x64 backend docker pipeline
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
pipeline {
|
||||
agent {
|
||||
node {
|
||||
label '构建机1'
|
||||
customWorkspace '/home/nanxunai/goujian/workspace/jizhang-backend-docker'
|
||||
}
|
||||
}
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
disableConcurrentBuilds()
|
||||
skipDefaultCheckout(true)
|
||||
timeout(time: 120, unit: 'MINUTES')
|
||||
buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '20'))
|
||||
}
|
||||
|
||||
environment {
|
||||
PATH = '/usr/local/bin:/usr/bin:/bin'
|
||||
HTTP_PROXY = 'http://192.168.5.200:7890'
|
||||
HTTPS_PROXY = 'http://192.168.5.200:7890'
|
||||
http_proxy = 'http://192.168.5.200:7890'
|
||||
https_proxy = 'http://192.168.5.200:7890'
|
||||
NO_PROXY = '127.0.0.1,localhost,192.168.5.8,192.168.5.100,.nxsir.cn'
|
||||
no_proxy = '127.0.0.1,localhost,192.168.5.8,192.168.5.100,.nxsir.cn'
|
||||
OPENLIST_CREDENTIALS = 'openlist_key'
|
||||
OPENLIST_BASE_URL = 'https://openlist.nxsir.cn'
|
||||
OPENLIST_REMOTE_DIR = '/yidongpan/构建产物/jizhang/backend-docker/linux-amd64'
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
deleteDir()
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
|
||||
stage('Metadata And Preflight') {
|
||||
steps {
|
||||
script {
|
||||
env.SHORT_SHA = sh(script: 'git rev-parse --short=8 HEAD', returnStdout: true).trim()
|
||||
env.FULL_SHA = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
|
||||
env.BUILD_DATE = sh(script: 'date -u +%Y-%m-%dT%H:%M:%SZ', returnStdout: true).trim()
|
||||
env.BACKEND_VERSION = sh(script: 'date -u +%Y%m%d-%H%M', returnStdout: true).trim()
|
||||
env.IMMUTABLE_TAG = "${env.BACKEND_VERSION}-b${env.BUILD_NUMBER}-${env.SHORT_SHA}"
|
||||
env.IMAGE_REF = "jizhi-backend:${env.IMMUTABLE_TAG}"
|
||||
env.ARCHIVE_BASENAME = "JiZhi-Backend-${env.IMMUTABLE_TAG}-linux-amd64.tar.gz"
|
||||
env.ARCHIVE_PATH = "${env.WORKSPACE}/artifacts/${env.ARCHIVE_BASENAME}"
|
||||
env.MANIFEST_PATH = "${env.WORKSPACE}/artifacts/JiZhi-Backend-${env.IMMUTABLE_TAG}-manifest.json"
|
||||
env.SMOKE_NETWORK = "jizhi-smoke-net-${env.BUILD_NUMBER}"
|
||||
env.SMOKE_MYSQL = "jizhi-smoke-mysql-${env.BUILD_NUMBER}"
|
||||
env.SMOKE_APP = "jizhi-smoke-app-${env.BUILD_NUMBER}"
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} ${env.IMMUTABLE_TAG}"
|
||||
currentBuild.description = 'linux/amd64 · Admin Web + ASP.NET API'
|
||||
}
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
test "$(uname -m)" = x86_64
|
||||
for command_name in git docker curl gzip sha256sum python3; do
|
||||
command -v "$command_name" >/dev/null
|
||||
done
|
||||
docker version >/dev/null
|
||||
docker buildx version
|
||||
test -f Dockerfile.backend
|
||||
test -f admin-web/package-lock.json
|
||||
mkdir -p artifacts
|
||||
available_kb=$(df -Pk "$WORKSPACE" | awk 'NR == 2 { print $4 }')
|
||||
test "$available_kb" -ge 8388608
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build linux-amd64 Image') {
|
||||
steps {
|
||||
retry(2) {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
docker buildx build \
|
||||
--platform linux/amd64 \
|
||||
--load \
|
||||
--file Dockerfile.backend \
|
||||
--build-arg "BUILD_VERSION=$IMMUTABLE_TAG" \
|
||||
--build-arg "VCS_REF=$FULL_SHA" \
|
||||
--build-arg "BUILD_DATE=$BUILD_DATE" \
|
||||
--tag "$IMAGE_REF" \
|
||||
.
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
test "$(docker image inspect "$IMAGE_REF" --format '{{.Os}}/{{.Architecture}}')" = 'linux/amd64'
|
||||
test "$(docker image inspect "$IMAGE_REF" --format '{{index .Config.Labels "org.opencontainers.image.revision"}}')" = "$FULL_SHA"
|
||||
docker run --rm --entrypoint /bin/sh "$IMAGE_REF" -c \
|
||||
'test -s /app/MiaoJiZhang.Api.dll && test -s /app/wwwroot/index.html'
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Smoke Test Image') {
|
||||
steps {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
docker network create "$SMOKE_NETWORK" >/dev/null
|
||||
docker run --detach \
|
||||
--name "$SMOKE_MYSQL" \
|
||||
--network "$SMOKE_NETWORK" \
|
||||
--env MYSQL_DATABASE=jizhi_smoke \
|
||||
--env MYSQL_USER=jizhi \
|
||||
--env MYSQL_PASSWORD=jizhi-smoke-password \
|
||||
--env MYSQL_ROOT_PASSWORD=jizhi-smoke-root-password \
|
||||
mysql:8.4 >/dev/null
|
||||
|
||||
mysql_ready=0
|
||||
for attempt in $(seq 1 60); do
|
||||
if docker exec "$SMOKE_MYSQL" mysqladmin ping \
|
||||
--user=jizhi --password=jizhi-smoke-password --silent; then
|
||||
mysql_ready=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "$mysql_ready" != 1 ]; then
|
||||
docker logs "$SMOKE_MYSQL" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker run --detach \
|
||||
--name "$SMOKE_APP" \
|
||||
--network "$SMOKE_NETWORK" \
|
||||
--publish 127.0.0.1::8080 \
|
||||
--env ASPNETCORE_ENVIRONMENT=Production \
|
||||
--env "ConnectionStrings__Default=Server=$SMOKE_MYSQL;Port=3306;Database=jizhi_smoke;User=jizhi;Password=jizhi-smoke-password" \
|
||||
--env Jwt__Secret=jizhi-smoke-jwt-secret-at-least-32-characters \
|
||||
--env Jwt__Issuer=MiaoJiZhang.Api \
|
||||
--env Jwt__Audience=MiaoJiZhang.App \
|
||||
--env Admin__BootstrapUsername=smoke_admin \
|
||||
--env Admin__BootstrapPassword=jizhi-smoke-admin-password \
|
||||
--env Admin__CookieSecure=false \
|
||||
--env "Build__Version=$IMMUTABLE_TAG" \
|
||||
"$IMAGE_REF" >/dev/null
|
||||
|
||||
host_port=$(docker port "$SMOKE_APP" 8080/tcp | sed -n 's/.*://p' | head -n 1)
|
||||
test -n "$host_port"
|
||||
app_ready=0
|
||||
for attempt in $(seq 1 90); do
|
||||
if curl --fail --silent --show-error \
|
||||
"http://127.0.0.1:$host_port/api/ping" >artifacts/smoke-ping.json; then
|
||||
app_ready=1
|
||||
break
|
||||
fi
|
||||
if ! docker inspect "$SMOKE_APP" --format '{{.State.Running}}' | grep -qx true; then
|
||||
docker logs "$SMOKE_APP" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "$app_ready" != 1 ]; then
|
||||
docker logs "$SMOKE_APP" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq "\"version\":\"$IMMUTABLE_TAG\"" artifacts/smoke-ping.json
|
||||
curl --fail --silent --show-error "http://127.0.0.1:$host_port/" \
|
||||
| grep -Fq '<div id="app"></div>'
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Package Image') {
|
||||
steps {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
docker save "$IMAGE_REF" | gzip -1 >"$ARCHIVE_PATH"
|
||||
gzip -t "$ARCHIVE_PATH"
|
||||
gzip -dc "$ARCHIVE_PATH" | tar -tf - | grep -q '^manifest.json$'
|
||||
sha256sum "$ARCHIVE_PATH" >"$ARCHIVE_PATH.sha256"
|
||||
image_id=$(docker image inspect "$IMAGE_REF" --format '{{.Id}}')
|
||||
archive_sha=$(awk '{print $1}' "$ARCHIVE_PATH.sha256")
|
||||
export image_id archive_sha
|
||||
python3 - <<'PY'
|
||||
import json, os, pathlib
|
||||
path = pathlib.Path(os.environ['MANIFEST_PATH'])
|
||||
payload = {
|
||||
'project': 'jizhang-backend',
|
||||
'buildNumber': os.environ['BUILD_NUMBER'],
|
||||
'version': os.environ['IMMUTABLE_TAG'],
|
||||
'commit': os.environ['FULL_SHA'],
|
||||
'platform': 'linux/amd64',
|
||||
'image': os.environ['IMAGE_REF'],
|
||||
'imageId': os.environ['image_id'],
|
||||
'archive': pathlib.Path(os.environ['ARCHIVE_PATH']).name,
|
||||
'archiveSha256': os.environ['archive_sha'],
|
||||
'builtAt': os.environ['BUILD_DATE'],
|
||||
'contents': ['admin-web', 'MiaoJiZhang.Api'],
|
||||
'smokeTests': ['image architecture', '/api/ping', 'admin web index'],
|
||||
}
|
||||
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
|
||||
PY
|
||||
sha256sum "$MANIFEST_PATH" >"$MANIFEST_PATH.sha256"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Upload To OpenList') {
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: "${OPENLIST_CREDENTIALS}", usernameVariable: 'OPENLIST_USERNAME', passwordVariable: 'OPENLIST_PASSWORD')]) {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
for artifact in \
|
||||
"$ARCHIVE_PATH" \
|
||||
"$ARCHIVE_PATH.sha256" \
|
||||
"$MANIFEST_PATH" \
|
||||
"$MANIFEST_PATH.sha256"; do
|
||||
./scripts/upload-openlist-artifact.sh "$artifact" "$OPENLIST_REMOTE_DIR"
|
||||
done
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
if (env.NODE_NAME?.trim()) {
|
||||
sh(
|
||||
script: '''
|
||||
docker rm --force "$SMOKE_APP" "$SMOKE_MYSQL" >/dev/null 2>&1 || true
|
||||
docker network rm "$SMOKE_NETWORK" >/dev/null 2>&1 || true
|
||||
docker image rm "$IMAGE_REF" >/dev/null 2>&1 || true
|
||||
''',
|
||||
returnStatus: true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
success {
|
||||
archiveArtifacts artifacts: 'artifacts/*.json,artifacts/*.sha256', fingerprint: true
|
||||
}
|
||||
cleanup {
|
||||
script {
|
||||
if (env.NODE_NAME?.trim()) {
|
||||
cleanWs(deleteDirs: true, notFailBuild: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user