mirror of
https://github.com/whyour/qinglong.git
synced 2026-11-02 06:58:52 +08:00
feat: ship verified platform upgrades with channel bundle releases
Package production dependencies per architecture for online and offline upgrades. Validate dependencies and isolated backend startup before readiness, repair once when permitted, and preserve service/data through failed preparation or rollback. Publish release bundles with master images and expose an opt-in develop build sharing the development index across Alpine and Debian images.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# ARMv7 is a source of application files only. None of its system libraries or
|
||||
# Node binaries are executed or copied into the ARMv6 runtime.
|
||||
ARG SOURCE_IMAGE
|
||||
FROM --platform=linux/arm/v7 ${SOURCE_IMAGE} AS source
|
||||
|
||||
# PRoot keeps ARM1176 execution isolated, without installing host binfmt handlers.
|
||||
FROM --platform=linux/amd64 debian:bookworm-slim@sha256:7c7b2c966bc9ee8cedfeef67e0e279108992c77681fa595db4a9d65c06ccc587 AS runtime
|
||||
COPY --from=source /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
RUN sed -i 's|http://deb.debian.org|https://deb.debian.org|g' /etc/apt/sources.list.d/debian.sources && \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
debootstrap gpg gpgv ca-certificates curl qemu-user-static && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
# Debian's PRoot 5.1 predates faccessat2, which modern QEMU uses for guest
|
||||
# access checks. Pin the upstream static tracer instead of misresolving paths.
|
||||
RUN curl -fsSL https://github.com/proot-me/proot/releases/download/v5.5.0/proot -o /usr/local/bin/proot && \
|
||||
echo 'db185300a9daabbce86aca7632d8c7fdc4249f6f153171954013b70c5f491ec0 /usr/local/bin/proot' | sha256sum -c - && \
|
||||
chmod 755 /usr/local/bin/proot && proot --version
|
||||
RUN curl -fsSL https://archive.raspbian.org/raspbian.public.key -o /tmp/raspbian.key && \
|
||||
echo 'ca59cd4f2bcbc3a1d41ba6815a02a8dc5c175467a59bd87edeac458f4a5345de /tmp/raspbian.key' | sha256sum -c - && \
|
||||
gpg --dearmor -o /usr/share/keyrings/raspbian.gpg /tmp/raspbian.key && \
|
||||
debootstrap --foreign --arch=armhf --variant=minbase \
|
||||
--keyring=/usr/share/keyrings/raspbian.gpg \
|
||||
bullseye /armv6 https://archive.raspbian.org/raspbian
|
||||
COPY docker/armv6-run /usr/local/bin/armv6-run
|
||||
RUN chmod 755 /usr/local/bin/armv6-run && \
|
||||
DEBOOTSTRAP_DIR=/debootstrap armv6-run /debootstrap/debootstrap --second-stage && \
|
||||
cp /usr/share/keyrings/raspbian.gpg /armv6/usr/share/keyrings/raspbian.gpg && \
|
||||
printf '%s\n' 'deb [signed-by=/usr/share/keyrings/raspbian.gpg] https://archive.raspbian.org/raspbian bullseye main' > /armv6/etc/apt/sources.list && \
|
||||
mkdir -p /armv6/etc/ssl/certs && \
|
||||
cp /etc/ssl/certs/ca-certificates.crt /armv6/etc/ssl/certs/ca-certificates.crt && \
|
||||
printf '127.0.0.1 localhost\n::1 localhost\n' > /armv6/etc/hosts && \
|
||||
printf '#!/bin/sh\nexit 101\n' > /armv6/usr/sbin/policy-rc.d && \
|
||||
chmod 755 /armv6/usr/sbin/policy-rc.d && \
|
||||
armv6-run /bin/sh -ec 'apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential binutils python3 ca-certificates curl git nginx procps jq sudo; rm -rf /var/lib/apt/lists/*'
|
||||
# This Node is a pinned CI/test prerequisite, never part of the upgrade archive.
|
||||
# Build against the minimum supported Node, including its bundled C++ headers.
|
||||
RUN curl -fsSL https://unofficial-builds.nodejs.org/download/release/v20.18.1/node-v20.18.1-linux-armv6l.tar.gz -o /tmp/node.tar.gz && \
|
||||
echo 'd4343a3d0fe23ddbd785537271bb0b60435edd50f5fba5bc580134592d51f011 /tmp/node.tar.gz' | sha256sum -c - && \
|
||||
tar -xzf /tmp/node.tar.gz -C /armv6/usr/local --strip-components=1 && rm /tmp/node.tar.gz
|
||||
COPY --from=source /ql/ /armv6/ql/
|
||||
# PM2 is JavaScript. Keep the ARMv6 Node/npm and the Raspbian system libraries.
|
||||
COPY --from=source /usr/local/lib/node_modules/pm2/ /armv6/usr/local/lib/node_modules/pm2/
|
||||
RUN ln -s ../lib/node_modules/pm2/bin/pm2 /armv6/usr/local/bin/pm2 && \
|
||||
armv6-run /bin/bash -ec 'ulimit -c 0; cd "$(readlink -f /ql/node_modules/sqlite3)"; CFLAGS="-march=armv6 -mfpu=vfp -mfloat-abi=hard" CXXFLAGS="-march=armv6 -mfpu=vfp -mfloat-abi=hard" npm_config_nodedir=/usr/local npm_config_build_from_source=true node --jitless -e '\''require("node:child_process").execFileSync(process.execPath,[require.resolve("@mapbox/node-pre-gyp/bin/node-pre-gyp"),"rebuild","--napi_build_version=6"],{stdio:"inherit"})'\''; readelf -A lib/binding/napi-v6-linux-arm/node_sqlite3.node | grep -E "Tag_CPU_arch: v6($|K)"; readelf -A lib/binding/napi-v6-linux-arm/node_sqlite3.node | grep "Tag_ABI_VFP_args: VFP registers"; rm -rf build; node --jitless -e '\''const p=process; if(p.config.variables.arm_version!=="6" && p.config.variables.arm_version!==6) process.exit(1); console.log(JSON.stringify({node:p.version,arm:p.config.variables.arm_version,float:p.config.variables.arm_float_abi,glibc:p.report.getReport().header.glibcVersionRuntime}));'\'''
|
||||
LABEL qinglong.qa.runtime="armv6-glibc-arm1176"
|
||||
ENTRYPOINT ["/usr/local/bin/armv6-run"]
|
||||
@@ -0,0 +1,30 @@
|
||||
# The image digest is supplied by the same workflow run, never a rolling tag.
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE} AS bundle
|
||||
ARG BASE_IMAGE
|
||||
ENV QL_BUNDLE_BASE_IMAGE=${BASE_IMAGE}
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
USER root
|
||||
# The generic "arm" sqlite release is ARMv7. ARMv6 must compile its own addon.
|
||||
RUN ulimit -c 0; if [ "$TARGETARCH/$TARGETVARIANT" = arm/v6 ]; then \
|
||||
apk add --no-cache --virtual .ql-bundle-build build-base binutils python3 nodejs-dev && \
|
||||
cd "$(readlink -f /ql/node_modules/sqlite3)" && \
|
||||
CFLAGS='-march=armv6 -mfpu=vfp -mfloat-abi=hard' \
|
||||
CXXFLAGS='-march=armv6 -mfpu=vfp -mfloat-abi=hard' \
|
||||
npm_config_nodedir=/usr npm_config_build_from_source=true QEMU_CPU=arm1176 \
|
||||
node -e 'require("node:child_process").execFileSync(process.execPath,[require.resolve("@mapbox/node-pre-gyp/bin/node-pre-gyp"),"rebuild","--napi_build_version=6"],{stdio:"inherit"})' && \
|
||||
readelf -A lib/binding/napi-v6-linux-arm/node_sqlite3.node | grep -E 'Tag_CPU_arch: v6($|K)' && \
|
||||
rm -rf build && \
|
||||
apk del .ql-bundle-build; \
|
||||
fi
|
||||
RUN ulimit -c 0; if [ "$TARGETARCH/$TARGETVARIANT" = arm/v6 ]; then \
|
||||
export QEMU_CPU=arm1176; \
|
||||
node --jitless /ql/scripts/build-upgrade-bundle.cjs /ql /out && \
|
||||
for archive in /out/*.tar.gz; do \
|
||||
node /ql/shell/install-bundle.cjs --root /tmp/ql-bundle-offline-smoke --file "$archive" --install && \
|
||||
rm -rf /tmp/ql-bundle-offline-smoke || exit 1; \
|
||||
done; \
|
||||
else node /ql/scripts/build-upgrade-bundle.cjs /ql /out; fi
|
||||
FROM scratch
|
||||
COPY --from=bundle /out/ /
|
||||
@@ -0,0 +1,13 @@
|
||||
# An immutable digest of the CI-only ARM1176 validator, built in the same run.
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE} AS bundle
|
||||
ARG BASE_IMAGE
|
||||
ENV QL_BUNDLE_BASE_IMAGE=${BASE_IMAGE}
|
||||
RUN ulimit -c 0; armv6-run /bin/bash -ec \
|
||||
'node --jitless /ql/scripts/build-upgrade-bundle.cjs /ql /out; \
|
||||
for archive in /out/*.tar.gz; do \
|
||||
node /ql/shell/install-bundle.cjs --root /tmp/ql-bundle-offline-smoke --file "$archive" --install; \
|
||||
rm -rf /tmp/ql-bundle-offline-smoke; \
|
||||
done'
|
||||
FROM scratch
|
||||
COPY --from=bundle /armv6/out/ /
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
export PROOT_NO_SECCOMP=1 QEMU_CPU=arm1176
|
||||
# Bind only kernel interfaces; all application files and writable state remain
|
||||
# inside this container's ARMv6 root. Child processes also pass through QEMU.
|
||||
exec proot -0 -q '/usr/bin/qemu-arm-static -cpu arm1176' \
|
||||
-r /armv6 -b /proc -b /dev -w / "$@"
|
||||
Reference in New Issue
Block a user