mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-15 19:57:07 +08:00
fix: 修复任务生命周期与调度就绪,优化执行和构建开销 (#3069)
* fix: harden task lifecycle and scheduler readiness * fix: confine log writes to the configured log directory * fix: verify complete build artifacts and untracked inputs * fix: reconcile scheduler state and make stop win startup races * fix: isolate cron generations and serialize scheduler recovery
This commit is contained in:
+10
-6
@@ -20,7 +20,7 @@ RUN set -x \
|
||||
&& npm_config_target_platform=linux \
|
||||
npm_config_target_arch="${NODE_ARCH}" \
|
||||
npm_config_target_libc=musl \
|
||||
pnpm install --prod
|
||||
pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM python:3.11-alpine
|
||||
|
||||
@@ -73,15 +73,19 @@ RUN set -x \
|
||||
&& ulimit -c 0
|
||||
|
||||
ARG SOURCE_COMMIT
|
||||
LABEL org.opencontainers.image.revision=${SOURCE_COMMIT}
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& if [ -n "${SOURCE_COMMIT}" ]; then git fetch --depth=1 origin "${SOURCE_COMMIT}" && git reset --hard FETCH_HEAD; fi \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh \
|
||||
&& git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
|
||||
&& mkdir -p ${QL_DIR}/static \
|
||||
&& cp -rf /static/* ${QL_DIR}/static \
|
||||
&& rm -rf /static
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh
|
||||
|
||||
# Downloaded by CI from the build-static job in this workflow run.
|
||||
COPY static/ /ql/static/
|
||||
COPY docker/verify-build.cjs docker/build-manifest.cjs /tmp/
|
||||
COPY --from=builder /tmp/build/pnpm-lock.yaml /tmp/dependency-lock.yaml
|
||||
RUN cd ${QL_DIR} && node /tmp/verify-build.cjs /tmp/dependency-lock.yaml
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
|
||||
+10
-6
@@ -20,7 +20,7 @@ RUN set -x \
|
||||
&& npm_config_target_platform=linux \
|
||||
npm_config_target_arch="${NODE_ARCH}" \
|
||||
npm_config_target_libc=musl \
|
||||
pnpm install --prod
|
||||
pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM python:3.10-alpine
|
||||
|
||||
@@ -73,15 +73,19 @@ RUN set -x \
|
||||
&& ulimit -c 0
|
||||
|
||||
ARG SOURCE_COMMIT
|
||||
LABEL org.opencontainers.image.revision=${SOURCE_COMMIT}
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& if [ -n "${SOURCE_COMMIT}" ]; then git fetch --depth=1 origin "${SOURCE_COMMIT}" && git reset --hard FETCH_HEAD; fi \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh \
|
||||
&& git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
|
||||
&& mkdir -p ${QL_DIR}/static \
|
||||
&& cp -rf /static/* ${QL_DIR}/static \
|
||||
&& rm -rf /static
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh
|
||||
|
||||
# Downloaded by CI from the build-static job in this workflow run.
|
||||
COPY static/ /ql/static/
|
||||
COPY docker/verify-build.cjs docker/build-manifest.cjs /tmp/
|
||||
COPY --from=builder /tmp/build/pnpm-lock.yaml /tmp/dependency-lock.yaml
|
||||
RUN cd ${QL_DIR} && node /tmp/verify-build.cjs /tmp/dependency-lock.yaml
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
|
||||
+14
-10
@@ -12,7 +12,7 @@ RUN set -x && \
|
||||
apt-get install --no-install-recommends -y libatomic1 && \
|
||||
npm i -g pnpm@8.3.1 && \
|
||||
cd /tmp/build && \
|
||||
pnpm install --prod
|
||||
pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM python:3.11.14-slim-bookworm
|
||||
|
||||
@@ -82,15 +82,19 @@ RUN mkdir -p ${QL_DIR} && \
|
||||
|
||||
USER qinglong
|
||||
ARG SOURCE_COMMIT
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \
|
||||
cd ${QL_DIR} && \
|
||||
cp -f .env.example .env && \
|
||||
chmod 777 ${QL_DIR}/shell/*.sh && \
|
||||
chmod 777 ${QL_DIR}/docker/*.sh && \
|
||||
git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \
|
||||
mkdir -p ${QL_DIR}/static && \
|
||||
cp -rf /tmp/static/* ${QL_DIR}/static && \
|
||||
rm -rf /tmp/static
|
||||
LABEL org.opencontainers.image.revision=${SOURCE_COMMIT}
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& if [ -n "${SOURCE_COMMIT}" ]; then git fetch --depth=1 origin "${SOURCE_COMMIT}" && git reset --hard FETCH_HEAD; fi \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh
|
||||
|
||||
# Downloaded by CI from the build-static job in this workflow run.
|
||||
COPY --chown=qinglong:qinglong static/ /ql/static/
|
||||
COPY docker/verify-build.cjs docker/build-manifest.cjs /tmp/
|
||||
COPY --from=builder /tmp/build/pnpm-lock.yaml /tmp/dependency-lock.yaml
|
||||
RUN cd ${QL_DIR} && node /tmp/verify-build.cjs /tmp/dependency-lock.yaml
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
|
||||
+14
-10
@@ -12,7 +12,7 @@ RUN set -x && \
|
||||
apt-get install --no-install-recommends -y libatomic1 && \
|
||||
npm i -g pnpm@8.3.1 && \
|
||||
cd /tmp/build && \
|
||||
pnpm install --prod
|
||||
pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM python:3.10-slim-bookworm
|
||||
|
||||
@@ -82,15 +82,19 @@ RUN mkdir -p ${QL_DIR} && \
|
||||
USER qinglong
|
||||
|
||||
ARG SOURCE_COMMIT
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \
|
||||
cd ${QL_DIR} && \
|
||||
cp -f .env.example .env && \
|
||||
chmod 777 ${QL_DIR}/shell/*.sh && \
|
||||
chmod 777 ${QL_DIR}/docker/*.sh && \
|
||||
git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \
|
||||
mkdir -p ${QL_DIR}/static && \
|
||||
cp -rf /tmp/static/* ${QL_DIR}/static && \
|
||||
rm -rf /tmp/static
|
||||
LABEL org.opencontainers.image.revision=${SOURCE_COMMIT}
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& if [ -n "${SOURCE_COMMIT}" ]; then git fetch --depth=1 origin "${SOURCE_COMMIT}" && git reset --hard FETCH_HEAD; fi \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh
|
||||
|
||||
# Downloaded by CI from the build-static job in this workflow run.
|
||||
COPY --chown=qinglong:qinglong static/ /ql/static/
|
||||
COPY docker/verify-build.cjs docker/build-manifest.cjs /tmp/
|
||||
COPY --from=builder /tmp/build/pnpm-lock.yaml /tmp/dependency-lock.yaml
|
||||
RUN cd ${QL_DIR} && node /tmp/verify-build.cjs /tmp/dependency-lock.yaml
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
|
||||
// Include every artifact, including imported backend modules and frontend chunks.
|
||||
function collectBuildFiles(root = 'static') {
|
||||
const files = {};
|
||||
function visit(relative) {
|
||||
for (const name of fs.readdirSync(path.join(root, relative)).sort()) {
|
||||
const entry = relative ? `${relative}/${name}` : name;
|
||||
if (entry === 'build-info.json') continue;
|
||||
const file = path.join(root, entry);
|
||||
const stat = fs.lstatSync(file);
|
||||
if (stat.isSymbolicLink())
|
||||
throw new Error(`Build output symlink: ${entry}`);
|
||||
if (stat.isDirectory()) visit(entry);
|
||||
else if (stat.isFile()) {
|
||||
files[entry] = crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(file))
|
||||
.digest('hex');
|
||||
} else throw new Error(`Unsupported build output: ${entry}`);
|
||||
}
|
||||
}
|
||||
visit('');
|
||||
return files;
|
||||
}
|
||||
module.exports = { collectBuildFiles };
|
||||
@@ -0,0 +1,49 @@
|
||||
const fs = require('node:fs');
|
||||
const crypto = require('node:crypto');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const { collectBuildFiles } = require('./build-manifest.cjs');
|
||||
|
||||
const manifest = JSON.parse(fs.readFileSync('static/build-info.json', 'utf8'));
|
||||
const sourceCommit = execFileSync('git', ['rev-parse', 'HEAD'], {
|
||||
encoding: 'utf8',
|
||||
}).trim();
|
||||
const lockfileSha256 = crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync('pnpm-lock.yaml'))
|
||||
.digest('hex');
|
||||
if (
|
||||
manifest.dirty !== false ||
|
||||
manifest.sourceCommit !== sourceCommit ||
|
||||
manifest.lockfileSha256 !== lockfileSha256
|
||||
) {
|
||||
throw new Error(
|
||||
'Build artifacts do not match the checked-out source and lockfile. Rebuild from the same clean commit.',
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[2]) {
|
||||
const dependencyLock = crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(process.argv[2]))
|
||||
.digest('hex');
|
||||
if (dependencyLock !== manifest.lockfileSha256)
|
||||
throw new Error(
|
||||
'Production dependencies were built from a different lockfile.',
|
||||
);
|
||||
}
|
||||
|
||||
const actual = collectBuildFiles();
|
||||
if (
|
||||
manifest.version !== 1 ||
|
||||
!manifest.files ||
|
||||
JSON.stringify(Object.keys(actual).sort()) !==
|
||||
JSON.stringify(Object.keys(manifest.files).sort())
|
||||
)
|
||||
throw new Error(
|
||||
'Build output file set mismatch. Rebuild the complete artifacts.',
|
||||
);
|
||||
for (const [file, hash] of Object.entries(actual)) {
|
||||
if (hash !== manifest.files[file])
|
||||
throw new Error(`Build output checksum mismatch: ${file}`);
|
||||
}
|
||||
console.log(`Verified build artifacts for ${sourceCommit}`);
|
||||
Reference in New Issue
Block a user