573 lines
26 KiB
Groovy
573 lines
26 KiB
Groovy
pipeline {
|
|
agent {
|
|
node {
|
|
label '构建机1'
|
|
customWorkspace '/home/nanxunai/goujian/workspace/liverecorder-release'
|
|
}
|
|
}
|
|
|
|
options {
|
|
timestamps()
|
|
disableConcurrentBuilds()
|
|
skipDefaultCheckout(true)
|
|
timeout(time: 180, unit: 'MINUTES')
|
|
buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '20'))
|
|
}
|
|
|
|
parameters {
|
|
booleanParam(
|
|
name: 'RUN_TESTS',
|
|
defaultValue: true,
|
|
description: '运行前端构建检查和 .NET 全量测试'
|
|
)
|
|
booleanParam(
|
|
name: 'BUILD_FNOS',
|
|
defaultValue: true,
|
|
description: '构建 fnOS x86_64 FPK 安装包'
|
|
)
|
|
booleanParam(
|
|
name: 'UPLOAD_FNOS',
|
|
defaultValue: true,
|
|
description: '将本次构建的 fnOS 包上传到 OpenList(仅 BUILD_FNOS 生效)'
|
|
)
|
|
choice(
|
|
name: 'API_IMAGE_PLATFORMS',
|
|
choices: ['amd64+arm64', 'amd64', 'arm64', 'none'],
|
|
description: '选择需要构建并推送的 API Docker 镜像架构'
|
|
)
|
|
choice(
|
|
name: 'WEB_IMAGE_PLATFORMS',
|
|
choices: ['amd64+arm64', 'amd64', 'arm64', 'none'],
|
|
description: '选择需要构建并推送的 Web Docker 镜像架构'
|
|
)
|
|
}
|
|
|
|
environment {
|
|
PATH = '/home/nanxunai/.local/bin:/usr/local/bin:/usr/bin:/bin'
|
|
REGISTRY_URL = 'reg.nxsir.cn'
|
|
API_IMAGE_REPO = 'reg.nxsir.cn/live_recorder/app-api'
|
|
WEB_IMAGE_REPO = 'reg.nxsir.cn/live_recorder/app-web'
|
|
HARBOR_CREDENTIALS = 'harbor_key'
|
|
OPENLIST_CREDENTIALS = 'openlist_key'
|
|
NODE_CREDENTIALS = 'bbb939ea-4f01-4b47-aecb-c5ee2a551ef4'
|
|
OPENLIST_BASE_URL = 'https://openlist.nxsir.cn'
|
|
OPENLIST_REMOTE_DIR = '/yidongpan/构建产物/liverecorder'
|
|
|
|
FNPACK_BIN = '/home/nanxunai/.local/bin/fnpack'
|
|
FNPACK_SHA256 = '54b97fa7b70968c4d05c79840f5daeff508957d0bb2062fdb0376d00d9615c93'
|
|
DOTNET = '/home/nanxunai/.local/bin/dotnet'
|
|
|
|
BUILDER_NAME = 'liverecorder-ci'
|
|
BUILDKIT_IMAGE = 'docker.m.daocloud.io/moby/buildkit:buildx-stable-1'
|
|
BINFMT_IMAGE = 'docker.m.daocloud.io/tonistiigi/binfmt:latest'
|
|
ARM_TEST_IMAGE = 'docker.m.daocloud.io/library/alpine:latest'
|
|
NODE_BUILD_IMAGE = 'docker.m.daocloud.io/library/node:22-alpine'
|
|
NGINX_IMAGE = 'docker.m.daocloud.io/library/nginx:1.27-alpine'
|
|
DOTNET_SDK_IMAGE = 'mcr.m.daocloud.io/dotnet/sdk:8.0-bookworm-slim'
|
|
DOTNET_RUNTIME_IMAGE = 'mcr.m.daocloud.io/dotnet/aspnet:8.0-bookworm-slim'
|
|
|
|
HTTP_PROXY_URL = 'http://192.168.5.200:7890'
|
|
NO_PROXY_HOSTS = '127.0.0.1,localhost,reg.nxsir.cn,gitea.nxsir.cn,openlist.nxsir.cn,mirrors.tuna.tsinghua.edu.cn,.tsinghua.edu.cn,mirrors.huaweicloud.com,.huaweicloud.com,mcr.m.daocloud.io,docker.m.daocloud.io,.m.daocloud.io'
|
|
|
|
CI_ROOT = "${WORKSPACE}/.ci"
|
|
CI_CACHE_ROOT = '/home/nanxunai/.cache/liverecorder-ci'
|
|
DOCKER_CONFIG = '/home/nanxunai/.cache/liverecorder-ci/docker'
|
|
NUGET_PACKAGES = '/home/nanxunai/.cache/liverecorder-ci/nuget-packages'
|
|
NUGET_HTTP_CACHE_PATH = '/home/nanxunai/.cache/liverecorder-ci/nuget-http'
|
|
NUGET_PLUGINS_CACHE_PATH = '/home/nanxunai/.cache/liverecorder-ci/nuget-plugins'
|
|
DOTNET_CLI_HOME = '/home/nanxunai/.cache/liverecorder-ci/dotnet-home'
|
|
NPM_CONFIG_CACHE = '/home/nanxunai/.cache/liverecorder-ci/npm-cache'
|
|
LIVERECORDER_NUGET_FEED = '/home/nanxunai/.cache/liverecorder-ci/nuget-feed'
|
|
LIVERECORDER_BUILD_TMPDIR = "${WORKSPACE}/.ci/fnos-tmp"
|
|
LIVERECORDER_VERIFY_TMPDIR = "${WORKSPACE}/.ci/fnos-verify"
|
|
LIVERECORDER_SKIP_NPM_CI = '1'
|
|
}
|
|
|
|
stages {
|
|
stage('Checkout') {
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
if [ -d "$WORKSPACE/.ci" ]; then
|
|
printf '%s\n' "$JENKINS_NODE_PASSWORD" | \
|
|
sudo -S -p '' chown -R -- "$(id -u):$(id -g)" "$WORKSPACE/.ci"
|
|
fi
|
|
'''
|
|
}
|
|
deleteDir()
|
|
checkout scm
|
|
}
|
|
}
|
|
|
|
stage('Metadata And Preflight') {
|
|
steps {
|
|
script {
|
|
if (!params.RUN_TESTS &&
|
|
!params.BUILD_FNOS &&
|
|
params.API_IMAGE_PLATFORMS == 'none' &&
|
|
params.WEB_IMAGE_PLATFORMS == 'none') {
|
|
error('Select at least one test or build output.')
|
|
}
|
|
|
|
env.APP_VERSION = sh(
|
|
script: "sed -n 's/^version=//p' fnos/manifest | head -n 1",
|
|
returnStdout: true
|
|
).trim()
|
|
env.SHORT_SHA = sh(script: 'git rev-parse --short=8 HEAD', returnStdout: true).trim()
|
|
if (!env.APP_VERSION || !env.SHORT_SHA) {
|
|
error('Unable to resolve fnOS version or Git commit.')
|
|
}
|
|
env.FNOS_VERSION = sh(
|
|
script: '''
|
|
set -euo pipefail
|
|
base=$(sed -n 's/^version=//p' fnos/manifest | head -n 1)
|
|
major=${base%%.*}
|
|
remainder=${base#*.}
|
|
minor=${remainder%%.*}
|
|
patch=${remainder#*.}
|
|
test "$base" = "$major.$minor.$patch" || {
|
|
echo "Invalid base version: $base" >&2
|
|
exit 1
|
|
}
|
|
case "$major.$minor.$patch" in
|
|
*[!0-9.]*|.*|*..*|*.) echo "Invalid base version: $base" >&2; exit 1 ;;
|
|
esac
|
|
test "$BUILD_NUMBER" -lt 100000 || {
|
|
echo 'BUILD_NUMBER must remain below 100000 for fnOS version ordering.' >&2
|
|
exit 1
|
|
}
|
|
ci_patch=$((patch * 100000 + BUILD_NUMBER))
|
|
printf '%s.%s.%s\n' "$major" "$minor" "$ci_patch"
|
|
''',
|
|
returnStdout: true
|
|
).trim()
|
|
env.IMMUTABLE_TAG = "${env.APP_VERSION}-b${env.BUILD_NUMBER}-${env.SHORT_SHA}"
|
|
env.FPK_BASENAME = "liverecorder-${env.FNOS_VERSION}-x86_64.fpk"
|
|
env.FPK_PATH = "${env.WORKSPACE}/artifacts/ci/${env.FPK_BASENAME}"
|
|
currentBuild.displayName = "#${env.BUILD_NUMBER} ${env.APP_VERSION} ${env.SHORT_SHA}"
|
|
currentBuild.description = "${env.IMMUTABLE_TAG}"
|
|
}
|
|
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
test "$(uname -m)" = x86_64
|
|
for command_name in git sudo docker curl python3 npm node sha256sum stat tar; do
|
|
command -v "$command_name" >/dev/null
|
|
done
|
|
test -x "$DOTNET"
|
|
test -x "$FNPACK_BIN"
|
|
printf '%s %s\n' "$FNPACK_SHA256" "$FNPACK_BIN" | sha256sum --check --status
|
|
./scripts/ci-docker.sh version >/dev/null
|
|
./scripts/ci-docker.sh buildx version
|
|
|
|
available_kb=$(df -Pk "$WORKSPACE" | awk 'NR == 2 { print $4 }')
|
|
if [ "$available_kb" -lt 2097152 ]; then
|
|
echo "At least 2 GiB free workspace disk is required; available KiB: $available_kb" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p \
|
|
"$CI_CACHE_ROOT" \
|
|
"$DOCKER_CONFIG" \
|
|
"$NUGET_PACKAGES" \
|
|
"$NUGET_HTTP_CACHE_PATH" \
|
|
"$NUGET_PLUGINS_CACHE_PATH" \
|
|
"$DOTNET_CLI_HOME" \
|
|
"$NPM_CONFIG_CACHE" \
|
|
"$LIVERECORDER_NUGET_FEED" \
|
|
"$LIVERECORDER_BUILD_TMPDIR" \
|
|
"$LIVERECORDER_VERIFY_TMPDIR" \
|
|
"$(dirname -- "$FPK_PATH")"
|
|
chmod 700 "$DOCKER_CONFIG"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Restore And Test') {
|
|
when {
|
|
expression { params.RUN_TESTS }
|
|
}
|
|
steps {
|
|
sh '''
|
|
set -euo pipefail
|
|
npm ci --prefix frontend
|
|
npm run build --prefix frontend
|
|
"$DOTNET" restore LiveRecorder.sln --disable-parallel
|
|
"$DOTNET" test LiveRecorder.sln -c Release --no-restore --logger 'console;verbosity=normal' /maxcpucount:1
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Prepare fnOS Dependencies') {
|
|
when {
|
|
expression { params.BUILD_FNOS && !params.RUN_TESTS }
|
|
}
|
|
steps {
|
|
sh '''
|
|
set -euo pipefail
|
|
npm ci --prefix frontend
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Build fnOS x64') {
|
|
when {
|
|
expression { params.BUILD_FNOS }
|
|
}
|
|
steps {
|
|
sh '''
|
|
set -euo pipefail
|
|
PACKAGE_VERSION="$FNOS_VERSION" FNPACK="$FNPACK_BIN" \
|
|
./scripts/build-fnos-package.sh "$FPK_PATH"
|
|
./scripts/verify-fnos-package.sh "$FPK_PATH" "$FNOS_VERSION"
|
|
test -s "$FPK_PATH.sha256"
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Prepare Docker Builder') {
|
|
when {
|
|
expression {
|
|
params.API_IMAGE_PLATFORMS != 'none' ||
|
|
params.WEB_IMAGE_PLATFORMS != 'none'
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
if [ "$API_IMAGE_PLATFORMS" = arm64 ] ||
|
|
[ "$API_IMAGE_PLATFORMS" = amd64+arm64 ] ||
|
|
[ "$WEB_IMAGE_PLATFORMS" = arm64 ] ||
|
|
[ "$WEB_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
if ! ./scripts/ci-docker.sh run --rm --platform linux/arm64 "$ARM_TEST_IMAGE" uname -m 2>/dev/null | grep -q aarch64; then
|
|
./scripts/ci-docker.sh run --privileged --rm "$BINFMT_IMAGE" --install arm64
|
|
fi
|
|
./scripts/ci-docker.sh run --rm --platform linux/arm64 "$ARM_TEST_IMAGE" uname -m | grep -q aarch64
|
|
fi
|
|
|
|
if ./scripts/ci-docker.sh buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then
|
|
./scripts/ci-docker.sh buildx use "$BUILDER_NAME"
|
|
else
|
|
./scripts/ci-docker.sh buildx create \
|
|
--name "$BUILDER_NAME" \
|
|
--driver docker-container \
|
|
--driver-opt "image=$BUILDKIT_IMAGE" \
|
|
--driver-opt network=host \
|
|
--driver-opt "env.http_proxy=$HTTP_PROXY_URL" \
|
|
--driver-opt "env.https_proxy=$HTTP_PROXY_URL" \
|
|
--driver-opt "env.HTTP_PROXY=$HTTP_PROXY_URL" \
|
|
--driver-opt "env.HTTPS_PROXY=$HTTP_PROXY_URL" \
|
|
--driver-opt "env.no_proxy=.daocloud.vip" \
|
|
--driver-opt "env.NO_PROXY=.daocloud.vip" \
|
|
--use
|
|
fi
|
|
./scripts/ci-docker.sh buildx inspect "$BUILDER_NAME" --bootstrap
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Prepare Harbor Authentication') {
|
|
when {
|
|
expression {
|
|
params.API_IMAGE_PLATFORMS != 'none' ||
|
|
params.WEB_IMAGE_PLATFORMS != 'none'
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${HARBOR_CREDENTIALS}",
|
|
usernameVariable: 'HARBOR_USERNAME',
|
|
passwordVariable: 'HARBOR_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
set +x
|
|
auth=$(printf '%s:%s' "$HARBOR_USERNAME" "$HARBOR_PASSWORD" | base64 -w0)
|
|
printf '{"auths":{"%s":{"auth":"%s"}}}\n' "$REGISTRY_URL" "$auth" >"$DOCKER_CONFIG/config.json"
|
|
chmod 600 "$DOCKER_CONFIG/config.json"
|
|
unset auth
|
|
set -x
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build And Push amd64') {
|
|
when {
|
|
expression {
|
|
params.API_IMAGE_PLATFORMS.contains('amd64') ||
|
|
params.WEB_IMAGE_PLATFORMS.contains('amd64')
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
arch=amd64
|
|
platform=linux/amd64
|
|
api_ref="$API_IMAGE_REPO:$IMMUTABLE_TAG-$arch"
|
|
web_ref="$WEB_IMAGE_REPO:$IMMUTABLE_TAG-$arch"
|
|
|
|
if [ "$API_IMAGE_PLATFORMS" = amd64 ] || [ "$API_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
./scripts/ci-docker.sh buildx build \
|
|
--builder "$BUILDER_NAME" --platform "$platform" --network host \
|
|
--progress=plain --provenance=false \
|
|
--build-arg DOTNET_SDK_IMAGE="$DOTNET_SDK_IMAGE" \
|
|
--build-arg DOTNET_RUNTIME_IMAGE="$DOTNET_RUNTIME_IMAGE" \
|
|
--build-arg HTTP_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg HTTPS_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg NO_PROXY="$NO_PROXY_HOSTS" \
|
|
--build-arg http_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg https_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg no_proxy="$NO_PROXY_HOSTS" \
|
|
-f src/LiveRecorder.WebApi/Dockerfile -t "$api_ref" --push .
|
|
fi
|
|
if [ "$WEB_IMAGE_PLATFORMS" = amd64 ] || [ "$WEB_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
./scripts/ci-docker.sh buildx build \
|
|
--builder "$BUILDER_NAME" --platform "$platform" --network host \
|
|
--progress=plain --provenance=false \
|
|
--build-arg NODE_IMAGE="$NODE_BUILD_IMAGE" \
|
|
--build-arg NGINX_IMAGE="$NGINX_IMAGE" \
|
|
--build-arg HTTP_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg HTTPS_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg NO_PROXY="$NO_PROXY_HOSTS" \
|
|
--build-arg http_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg https_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg no_proxy="$NO_PROXY_HOSTS" \
|
|
--build-arg VITE_API_BASE_URL=/api \
|
|
-f frontend/Dockerfile -t "$web_ref" --push frontend
|
|
fi
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build And Push arm64') {
|
|
when {
|
|
expression {
|
|
params.API_IMAGE_PLATFORMS.contains('arm64') ||
|
|
params.WEB_IMAGE_PLATFORMS.contains('arm64')
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
arch=arm64
|
|
platform=linux/arm64
|
|
api_ref="$API_IMAGE_REPO:$IMMUTABLE_TAG-$arch"
|
|
web_ref="$WEB_IMAGE_REPO:$IMMUTABLE_TAG-$arch"
|
|
|
|
if [ "$API_IMAGE_PLATFORMS" = arm64 ] || [ "$API_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
./scripts/ci-docker.sh buildx build \
|
|
--builder "$BUILDER_NAME" --platform "$platform" --network host \
|
|
--progress=plain --provenance=false \
|
|
--build-arg DOTNET_SDK_IMAGE="$DOTNET_SDK_IMAGE" \
|
|
--build-arg DOTNET_RUNTIME_IMAGE="$DOTNET_RUNTIME_IMAGE" \
|
|
--build-arg HTTP_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg HTTPS_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg NO_PROXY="$NO_PROXY_HOSTS" \
|
|
--build-arg http_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg https_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg no_proxy="$NO_PROXY_HOSTS" \
|
|
-f src/LiveRecorder.WebApi/Dockerfile -t "$api_ref" --push .
|
|
fi
|
|
if [ "$WEB_IMAGE_PLATFORMS" = arm64 ] || [ "$WEB_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
./scripts/ci-docker.sh buildx build \
|
|
--builder "$BUILDER_NAME" --platform "$platform" --network host \
|
|
--progress=plain --provenance=false \
|
|
--build-arg NODE_IMAGE="$NODE_BUILD_IMAGE" \
|
|
--build-arg NGINX_IMAGE="$NGINX_IMAGE" \
|
|
--build-arg HTTP_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg HTTPS_PROXY="$HTTP_PROXY_URL" \
|
|
--build-arg NO_PROXY="$NO_PROXY_HOSTS" \
|
|
--build-arg http_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg https_proxy="$HTTP_PROXY_URL" \
|
|
--build-arg no_proxy="$NO_PROXY_HOSTS" \
|
|
--build-arg VITE_API_BASE_URL=/api \
|
|
-f frontend/Dockerfile -t "$web_ref" --push frontend
|
|
fi
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Publish Multi-arch Manifests') {
|
|
when {
|
|
expression {
|
|
params.API_IMAGE_PLATFORMS == 'amd64+arm64' ||
|
|
params.WEB_IMAGE_PLATFORMS == 'amd64+arm64'
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
publish_manifest() {
|
|
repository=$1
|
|
immutable_ref="$repository:$IMMUTABLE_TAG"
|
|
./scripts/ci-docker.sh buildx imagetools create \
|
|
--tag "$immutable_ref" \
|
|
"$immutable_ref-amd64" "$immutable_ref-arm64"
|
|
|
|
manifest=$(./scripts/ci-docker.sh buildx imagetools inspect --raw "$immutable_ref")
|
|
printf '%s' "$manifest" | grep -q '"architecture"[[:space:]]*:[[:space:]]*"amd64"'
|
|
printf '%s' "$manifest" | grep -q '"architecture"[[:space:]]*:[[:space:]]*"arm64"'
|
|
./scripts/ci-docker.sh buildx imagetools create \
|
|
--tag "$repository:$APP_VERSION" --tag "$repository:latest" \
|
|
"$immutable_ref"
|
|
}
|
|
|
|
if [ "$API_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
publish_manifest "$API_IMAGE_REPO"
|
|
fi
|
|
if [ "$WEB_IMAGE_PLATFORMS" = amd64+arm64 ]; then
|
|
publish_manifest "$WEB_IMAGE_REPO"
|
|
fi
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Upload fnOS Artifact') {
|
|
when {
|
|
expression { params.BUILD_FNOS && params.UPLOAD_FNOS }
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${OPENLIST_CREDENTIALS}",
|
|
usernameVariable: 'OPENLIST_USERNAME',
|
|
passwordVariable: 'OPENLIST_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set -euo pipefail
|
|
./scripts/upload-openlist-artifact.sh "$FPK_PATH" "$OPENLIST_REMOTE_DIR"
|
|
./scripts/upload-openlist-artifact.sh "$FPK_PATH.sha256" "$OPENLIST_REMOTE_DIR"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Build Summary') {
|
|
steps {
|
|
script {
|
|
def summary = [
|
|
'Commit: ' + sh(script: 'git rev-parse HEAD', returnStdout: true).trim(),
|
|
'Product version: ' + env.APP_VERSION,
|
|
'Tests: ' + (params.RUN_TESTS ? 'executed' : 'skipped')
|
|
]
|
|
if (params.BUILD_FNOS) {
|
|
def checksum = sh(
|
|
script: "cut -d ' ' -f 1 '" + env.FPK_PATH + ".sha256'",
|
|
returnStdout: true
|
|
).trim()
|
|
summary.addAll([
|
|
'fnOS package version: ' + env.FNOS_VERSION,
|
|
'fnOS: ' + env.FPK_BASENAME,
|
|
'Checksum: ' + checksum
|
|
])
|
|
if (params.UPLOAD_FNOS) {
|
|
summary << 'OpenList: ' + env.OPENLIST_BASE_URL + env.OPENLIST_REMOTE_DIR + '/' + env.FPK_BASENAME
|
|
}
|
|
}
|
|
if (params.API_IMAGE_PLATFORMS != 'none') {
|
|
summary << 'API platforms: ' + params.API_IMAGE_PLATFORMS
|
|
if (params.API_IMAGE_PLATFORMS.contains('amd64')) {
|
|
summary << 'API amd64: ' + env.API_IMAGE_REPO + ':' + env.IMMUTABLE_TAG + '-amd64'
|
|
}
|
|
if (params.API_IMAGE_PLATFORMS.contains('arm64')) {
|
|
summary << 'API arm64: ' + env.API_IMAGE_REPO + ':' + env.IMMUTABLE_TAG + '-arm64'
|
|
}
|
|
if (params.API_IMAGE_PLATFORMS == 'amd64+arm64') {
|
|
summary << 'API multi-arch: ' + env.API_IMAGE_REPO + ':' + env.IMMUTABLE_TAG
|
|
}
|
|
}
|
|
if (params.WEB_IMAGE_PLATFORMS != 'none') {
|
|
summary << 'Web platforms: ' + params.WEB_IMAGE_PLATFORMS
|
|
if (params.WEB_IMAGE_PLATFORMS.contains('amd64')) {
|
|
summary << 'Web amd64: ' + env.WEB_IMAGE_REPO + ':' + env.IMMUTABLE_TAG + '-amd64'
|
|
}
|
|
if (params.WEB_IMAGE_PLATFORMS.contains('arm64')) {
|
|
summary << 'Web arm64: ' + env.WEB_IMAGE_REPO + ':' + env.IMMUTABLE_TAG + '-arm64'
|
|
}
|
|
if (params.WEB_IMAGE_PLATFORMS == 'amd64+arm64') {
|
|
summary << 'Web multi-arch: ' + env.WEB_IMAGE_REPO + ':' + env.IMMUTABLE_TAG
|
|
}
|
|
}
|
|
echo summary.join('\n')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
success {
|
|
echo "LiveRecorder ${env.IMMUTABLE_TAG} selected pipeline completed successfully."
|
|
}
|
|
failure {
|
|
echo 'LiveRecorder pipeline failed; stable Docker tags were only updated if all architecture builds succeeded.'
|
|
}
|
|
always {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: "${NODE_CREDENTIALS}",
|
|
usernameVariable: 'JENKINS_NODE_USERNAME',
|
|
passwordVariable: 'JENKINS_NODE_PASSWORD'
|
|
)]) {
|
|
sh '''
|
|
set +e
|
|
if [ -x ./scripts/ci-docker.sh ] && [ -n "${IMMUTABLE_TAG:-}" ]; then
|
|
for repository in "$API_IMAGE_REPO" "$WEB_IMAGE_REPO"; do
|
|
for suffix in amd64 arm64; do
|
|
./scripts/ci-docker.sh rmi "$repository:$IMMUTABLE_TAG-$suffix" >/dev/null 2>&1 || true
|
|
done
|
|
./scripts/ci-docker.sh manifest rm "$repository:$IMMUTABLE_TAG" >/dev/null 2>&1 || true
|
|
done
|
|
fi
|
|
if [ -f "$DOCKER_CONFIG/config.json" ]; then
|
|
: >"$DOCKER_CONFIG/config.json"
|
|
fi
|
|
if [ -x ./scripts/ci-docker.sh ] && [ -n "${BUILDER_NAME:-}" ]; then
|
|
./scripts/ci-docker.sh buildx prune \
|
|
--builder "$BUILDER_NAME" --filter until=168h \
|
|
--keep-storage 8gb --force >/dev/null 2>&1 || true
|
|
fi
|
|
if [ -d "${CI_ROOT:-}" ]; then
|
|
printf '%s\n' "$JENKINS_NODE_PASSWORD" | \
|
|
sudo -S -p '' chown -R -- "$(id -u):$(id -g)" "$CI_ROOT" || true
|
|
fi
|
|
'''
|
|
}
|
|
cleanWs(deleteDirs: true, notFailBuild: true)
|
|
}
|
|
}
|
|
}
|