diff --git a/.github/workflows/release-readiness.yml b/.github/workflows/release-readiness.yml new file mode 100644 index 00000000..fcb6784c --- /dev/null +++ b/.github/workflows/release-readiness.yml @@ -0,0 +1,136 @@ +name: Release readiness + +on: + pull_request: + paths: + - '.github/workflows/release-readiness.yml' + - 'package.json' + - 'pnpm-lock.yaml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: release-readiness-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + regression: + uses: ./.github/workflows/validate.yml + + static: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - uses: pnpm/action-setup@v6 + with: + version: '8.3.1' + - uses: actions/setup-node@v6 + with: + node-version: '20' + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm build:front && pnpm build:back && pnpm build:info + - uses: actions/upload-artifact@v6 + with: + name: release-readiness-static + path: static/ + include-hidden-files: true + if-no-files-found: error + + images: + needs: [regression, static] + runs-on: ubuntu-22.04 + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - image: alpine + file: docker/Dockerfile + cache: alpine + platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x,linux/386 + - image: debian + file: docker/Dockerfile.debian + cache: debian + platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x + - image: alpine-python3.10 + file: docker/Dockerfile.310 + cache: alpine-python3.10 + platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x,linux/386 + - image: debian-python3.10 + file: docker/Dockerfile.debian310 + cache: debian-python3.10 + platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Record source + id: source + env: + SOURCE_URL: ${{ github.event.pull_request.head.repo.clone_url || format('https://github.com/{0}.git', github.repository) }} + SOURCE_BRANCH: ${{ github.head_ref || github.ref_name }} + run: | + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + echo "url=$SOURCE_URL" >> "$GITHUB_OUTPUT" + echo "branch=$SOURCE_BRANCH" >> "$GITHUB_OUTPUT" + - uses: actions/download-artifact@v7 + with: + name: release-readiness-static + path: static/ + - uses: docker/setup-qemu-action@v4 + with: + cache-image: false + - uses: docker/setup-buildx-action@v4 + - name: Build every release architecture + uses: docker/build-push-action@v7 + with: + context: . + file: ${{ matrix.file }} + platforms: ${{ matrix.platforms }} + build-args: | + QL_URL=${{ steps.source.outputs.url }} + QL_BRANCH=${{ steps.source.outputs.branch }} + SOURCE_COMMIT=${{ steps.source.outputs.sha }} + push: false + provenance: false + sbom: false + cache-from: type=registry,ref=whyour/qinglong:cache-${{ matrix.cache }} + - name: Load native image for runtime verification + uses: docker/build-push-action@v7 + with: + context: . + file: ${{ matrix.file }} + platforms: linux/amd64 + build-args: | + QL_URL=${{ steps.source.outputs.url }} + QL_BRANCH=${{ steps.source.outputs.branch }} + SOURCE_COMMIT=${{ steps.source.outputs.sha }} + tags: qinglong-readiness:${{ matrix.image }} + load: true + push: false + provenance: false + sbom: false + - name: Verify native production runtime without external networking + run: | + docker run --rm --network none --entrypoint node \ + qinglong-readiness:${{ matrix.image }} \ + -e 'const assert=require("node:assert/strict"); const {createRequire}=require("node:module"); for(const [name,want] of Object.entries({"@grpc/grpc-js":"1.14.5",compression:"1.8.2",multer:"2.3.0",sequelize:"6.37.8"}))assert.equal(require(name+"/package.json").version,want); const expressRequire=createRequire(require.resolve("express")); assert.equal(expressRequire("proxy-addr/package.json").version,"2.0.8"); const sockRequire=createRequire(require.resolve("sockjs")); assert.equal(sockRequire("websocket-driver/package.json").version,"0.7.5"); require("sqlite3"); require("nodemailer"); require("undici"); console.log("Minimal production patches and runtime imports verified")' + - name: Export native Debian image for isolated dev validation + if: matrix.image == 'debian' + run: | + docker save qinglong-readiness:debian | gzip -1 > release-readiness-debian-image.tar.gz + sha256sum release-readiness-debian-image.tar.gz > release-readiness-debian-image.sha256 + docker image inspect qinglong-readiness:debian > release-readiness-debian-image.json + - uses: actions/upload-artifact@v6 + if: matrix.image == 'debian' + with: + name: release-readiness-debian-image + path: release-readiness-debian-image.* + compression-level: 0 + if-no-files-found: error diff --git a/cli/package-lock.json b/cli/package-lock.json index 8c1f3011..1f8e5034 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@whyour/qinglong-cli", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@whyour/qinglong-cli", - "version": "0.1.1", + "version": "0.1.2", "bin": { "ql": "dist/npm/ql.js" }, diff --git a/cli/package.json b/cli/package.json index 46ab12bd..7185a111 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@whyour/qinglong-cli", - "version": "0.1.1", + "version": "0.1.2", "description": "Authenticated remote management CLI for QingLong 2.x", "type": "commonjs", "bin": { diff --git a/docs/releases/2.23.0.md b/docs/releases/2.23.0.md new file mode 100644 index 00000000..fe77872a --- /dev/null +++ b/docs/releases/2.23.0.md @@ -0,0 +1,33 @@ +# 青龙 2.23.0 + +## 新增功能 + +- 脚本历史版本:查看、对比和恢复历史内容;每个脚本保留最多 20 个版本,支持不超过 1 MiB 的 UTF-8 文本脚本。恢复前校验当前内容哈希,发生并发修改时返回冲突,避免覆盖新内容。 +- 独立远程 CLI 0.1.2 增加脚本历史查看、对比和恢复命令,并完善资源冲突及认证错误反馈。最低要求仍为 Node.js 22.12。 + +## 修复与优化 + +- 普通任务、额外规则和订阅的 Node 调度路径迁移到 node-cron。保留旧规则标准化、日期/星期 OR、L/# 等兼容行为,补充 `@annually`、`@midnight`、`@minutely`。 +- 禁用、删除或修改任务后,旧规则对应的排队执行不会再启动。完善任务进程超时、停止回收、Shell before/after 环境传递和退出状态。 +- 修复系统令牌未主动续期和认证缓存并发更新:定时生成与启动后重新计时会主动续期,避免长期运行后失效。Shell API 每次请求检查令牌,遇到 401 时刷新并重试一次;其他错误不自动重试。 +- 降低任务启动开销;JS/Python 的 QLAPI 通知依赖在首次通知调用时加载,减少不发送通知的任务启动成本。 +- 加固环境变量名和字面值、订阅参数、路径访问及 SSH 配置输入;保留合法的中文及分支别名兼容。 +- 修复页面升级、`ql update` 和静态资源的版本同步,完善升级暂存校验及应用、完整备份与恢复流程。 +- 修复 Debian 上的依赖安装、通知和调度回归,补齐前端缺失的中英文翻译,改进 Gitee/GitLab 静态资源镜像同步检查。 +- 最小升级 compression、multer、@grpc/grpc-js、proxy-addr、websocket-driver、sequelize,修补现有 HTTP、上传、代理信任、WebSocket、gRPC 和 JSON 查询路径相关风险。完整依赖治理、Node/pnpm 迁移及工具打包调整延期。 + +## 升级说明 + +升级前停止面板与任务,备份完整持久化数据目录,包括数据库、脚本、配置、日志、依赖缓存及已有的 `script-history` 目录,并记录原镜像摘要和启动配置。使用面板备份接口时,确认选择了脚本、配置和日志;仅备份数据库不能恢复全部数据。 + +升级后试运行代表性的 JavaScript、Python、Shell 任务,检查订阅、环境变量、依赖安装以及通知。存在夏令时的时区采用 node-cron 行为:跳过春季不存在的当地时间,秋季重复小时不重复执行;Asia/Shanghai 不受影响。调度器不提供进程退出期间的持久化补跑保证。 + +任务超时会终止普通子进程所在的进程组并记录退出状态 124;主动创建独立会话/进程组的程序不在该边界内。并发账号执行模式保留既有汇总退出状态。 + +本次沿用现有 Dockerfile 和 pnpm 8.3.1。默认 Debian 镜像使用 Debian 12 与 Node.js 20,默认 Alpine 镜像的 Node.js 由 APK 提供;默认 Python 为 3.11。Node 20 与 Node 18 构建运行时已结束上游维护,相关迁移延期,功能验证通过不代表全部依赖风险清零。 + +2.23.0 是 Python 3.10 兼容镜像的最后一个版本。`2.23.0-python3.10` 和 `2.23.0-debian-python3.10` 发布后停止维护,不再提供后续更新;请验证脚本和依赖后迁移至默认 Python 3.11 镜像。 + +主包 `@whyour/qinglong` 与独立远程包 `@whyour/qinglong-cli` 的 `ql` 命令用途不同。在已有面板的主机上使用独立 npm prefix 或 `npm exec --package=@whyour/qinglong-cli -- ql ...`,避免覆盖面板内置入口。 + +本文及版本元数据为发布准备稿。镜像验证和本候选独立 dev 长跑通过后,再填写正式发布时间并执行发布。 diff --git a/package.json b/package.json index 1c96e5b6..7a789fb6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@whyour/qinglong", "packageManager": "pnpm@8.3.1", - "version": "2.22.0", + "version": "2.23.0", "description": "Timed task management platform supporting Python3, JavaScript, Shell, Typescript", "repository": { "type": "git", diff --git a/version.yaml b/version.yaml index 45328a27..e19f1d28 100644 --- a/version.yaml +++ b/version.yaml @@ -1,22 +1,15 @@ -version: 2.22.0 -changeLogLink: https://github.com/whyour/qinglong/blob/master/docs/releases/2.22.0.md -publishTime: 2026-09-27 1643 +version: 2.23.0 +changeLogLink: https://github.com/whyour/qinglong/blob/master/docs/releases/2.23.0.md +publishTime: "" changeLog: | - 修复更新后含 ? 的历史定时规则导致面板 503,以及 ql check 的 printf 报错(#3078) - 无效任务不再阻止面板启动;统一调度表达式校验并修复扩展规则的调度器分配 - 1. 新增独立远程 CLI,覆盖任务、订阅、环境变量、配置、脚本等 OpenAPI - 2. 新增可选面板内部 TypeScript 工具,默认保留 Shell 入口 - 3. 仪表盘支持查看今日成功和失败任务明细 - 4. 新增客户端 IP 与代理链诊断、可信代理设置和登录 IP 黑名单 - 5. 新增历史运行实例和统计数据清理预览、手动清理及数据库压缩 - 6. 新增 WPUSH 通知,支持面板、JS、Python 和 gRPC - 7. 新增 ql log 服务日志命令,优化大目录和大日志读取,修复中文日志名 - 8. 修复任务结束后大日志尾部未读完的问题 - 9. 修复调度锁、调度恢复、执行就绪和任务停止竞争,准确反馈停止失败 - 10. 加固密码存储、JWT 与两步验证、文件访问和旧文件接口 - 11. 修复登录令牌过期后页面反复刷新,完善任务参数校验提示 - 12. 降低常驻依赖、指标缓存和重复 WebSocket 鉴权开销 - 13. 优化 Node 路径缓存及 Alpine 并发缓存锁,修复依赖安装状态 - 14. 改善启动错误诊断,保留用户 .env 配置,严格处理数据库迁移失败 - 15. 镜像增加来源证明、SBOM 和构建产物一致性校验,改进多架构构建 - 16. 升级前请备份完整数据;密码迁移后回退旧版需恢复升级前数据或重置账号 + 1. 新增脚本历史版本查看、对比与恢复,支持 CLI 和 OpenAPI;恢复前检查当前内容哈希,避免覆盖并发修改 + 2. 普通任务与订阅调度迁移到 node-cron,保留旧规则兼容及日期/星期 OR 语义,补充宏和夏令时说明 + 3. 修复已失效排队任务仍被执行的问题,完善任务进程超时、停止和 Shell 环境传递 + 4. 修复系统令牌主动续期与认证缓存并发更新;Shell API 遇到 401 时刷新令牌并重试一次 + 5. 降低任务启动开销,JS/Python 通知依赖改为首次调用时加载 + 6. 加固环境变量、订阅参数、文件访问和 SSH 配置输入,改善资源冲突与 CLI 错误反馈 + 7. 修复页面升级、ql update 与静态资源版本同步,完善离线备份和升级产物校验 + 8. 修复 Debian 依赖安装、通知与调度回归,补齐前端中英文翻译 + 9. 最小修补 HTTP 压缩、上传、gRPC、代理 IP、WebSocket 和 JSON 查询相关依赖漏洞 + 10. 远程 CLI 更新至 0.1.2,新增脚本历史命令,最低 Node.js 版本仍为 22.12 + 11. 本次为 Python 3.10 兼容镜像的最后一个版本,发布后停止维护;升级前备份完整数据并验证脚本、依赖和通知