mirror of
https://github.com/whyour/qinglong.git
synced 2026-11-02 15:27:58 +08:00
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.
92 lines
3.2 KiB
JavaScript
92 lines
3.2 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs/promises');
|
|
const path = require('node:path');
|
|
const {
|
|
manualUpgradeFixture,
|
|
} = require('../helpers/manual-upgrade-fixture.cjs');
|
|
const artifacts = require('../../cli/src/internal/maintenance/upgradeArtifacts.cjs');
|
|
const { installBundle } = require('../../shell/install-bundle.cjs');
|
|
|
|
for (const scenario of ['cancel-new-start', 'both-starts-fail'])
|
|
test(`offline application restores files and records failure: ${scenario}`, async (t) => {
|
|
const f = await manualUpgradeFixture(t);
|
|
t.mock.method(artifacts, 'stageUpgrade', async () => f.selected);
|
|
const journal = path.join(f.root, 'lifecycle.jsonl');
|
|
await fs.writeFile(
|
|
path.join(f.root, 'lifecycle.cjs'),
|
|
`
|
|
const fs=require('node:fs'),path=require('node:path');
|
|
const root=process.env.QL_DIR,op=process.argv[2];
|
|
const version=fs.readFileSync(path.join(root,'static/build/app.js'),'utf8');
|
|
fs.appendFileSync(${JSON.stringify(
|
|
journal,
|
|
)},JSON.stringify({op,version})+'\\n');
|
|
if(op==='start') {
|
|
if(${JSON.stringify(scenario)}==='both-starts-fail') process.exit(9);
|
|
if(version==='new backend') {
|
|
process.kill(Number(process.env.TEST_INSTALLER_PID),'SIGTERM');
|
|
setTimeout(()=>{},10000);
|
|
}
|
|
}
|
|
`,
|
|
);
|
|
await fs.writeFile(
|
|
path.join(f.root, 'shell/share.sh'),
|
|
`
|
|
import_config(){ :; }
|
|
delete_pm2(){ "$TEST_NODE" "$QL_DIR/lifecycle.cjs" stop; }
|
|
reload_pm2(){ "$TEST_NODE" "$QL_DIR/lifecycle.cjs" start; }
|
|
`,
|
|
);
|
|
await assert.rejects(
|
|
installBundle(
|
|
['--root', f.root, '--file', path.join(f.root, 'unused.tar.gz')],
|
|
{
|
|
...f.env,
|
|
TEST_NODE: process.execPath,
|
|
TEST_INSTALLER_PID: String(process.pid),
|
|
},
|
|
),
|
|
scenario === 'cancel-new-start' ? /离线升级已中断/ : /failed/,
|
|
);
|
|
const calls = (await fs.readFile(journal, 'utf8'))
|
|
.trim()
|
|
.split('\n')
|
|
.map(JSON.parse);
|
|
assert.deepEqual(calls, [
|
|
{ op: 'stop', version: 'old backend' },
|
|
{ op: 'start', version: 'new backend' },
|
|
{ op: 'stop', version: 'new backend' },
|
|
{ op: 'start', version: 'old backend' },
|
|
]);
|
|
assert.equal((await f.state()).status, 'failed');
|
|
assert.equal(
|
|
await fs.readFile(path.join(f.root, 'static/build/app.js'), 'utf8'),
|
|
'old backend',
|
|
);
|
|
assert.equal(
|
|
await fs.readFile(path.join(f.root, 'node_modules/old'), 'utf8'),
|
|
'old dependency',
|
|
);
|
|
assert.equal(
|
|
await fs.readFile(path.join(f.root, '.env'), 'utf8'),
|
|
'preserve configuration',
|
|
);
|
|
assert.equal(
|
|
await fs.readFile(path.join(f.root, 'data/keep'), 'utf8'),
|
|
'preserve data',
|
|
);
|
|
await fs.access(path.join(f.tmp, 'upgrade-ready-develop.json'));
|
|
await assert.rejects(fs.access(path.join(f.tmp, 'upgrade.lock')), {
|
|
code: 'ENOENT',
|
|
});
|
|
await assert.rejects(
|
|
fs.access(path.join(f.tmp, 'panel-job-launch/upgrade.lock')),
|
|
{ code: 'ENOENT' },
|
|
);
|
|
assert.ok(
|
|
!(await fs.readdir(f.root)).some((n) => n.includes('.ql-backup-')),
|
|
);
|
|
});
|