mirror of
https://github.com/whyour/qinglong.git
synced 2026-11-08 04:23:05 +08:00
Use MikroORM's official Node SQLite dialect with bounded database isolation, preserve legacy schema and data for rollback, and require Node 22.17 or newer. Update Debian runtimes and retain ARMv6 musl compatibility without a SQLite addon. Complete short archive writes, stop orphaned SQL workers after owner death, preserve buffered startup requests, and prestart the archived backend on every platform. Cover real package installation, migration, failure recovery, and legacy database compatibility. Keep authentication storage failures distinct from expired sessions with a pinned Keyv runtime, propagated cache errors, and retryable HTTP 503 responses. Preserve genuine 401 failures and verify legacy cache bytes, initialization safety, and session recovery.
137 lines
5.6 KiB
YAML
137 lines
5.6 KiB
YAML
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: '22'
|
|
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.4.0"}))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"); assert.equal(typeof require("@mikro-orm/sql").NodeSqliteDialect,"function"); const db=new (require("node:sqlite").DatabaseSync)(":memory:"); assert.equal(db.prepare("SELECT 42 AS value").get().value,42); db.close(); 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
|