forked from wangziqi/gongxue-base
110 lines
4.4 KiB
JavaScript
110 lines
4.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
|
|
const repoRoot = process.cwd();
|
|
const scriptPath = path.join(repoRoot, 'scripts', 'taro-h5-release-manifest.js');
|
|
const distRoot = path.join(repoRoot, 'apps', 'taro', 'dist');
|
|
|
|
function run(args = []) {
|
|
return spawnSync(process.execPath, [scriptPath, '--json', ...args], {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
env: {
|
|
PATH: process.env.PATH || '',
|
|
Path: process.env.Path || '',
|
|
SystemRoot: process.env.SystemRoot || '',
|
|
ComSpec: process.env.ComSpec || '',
|
|
TEMP: process.env.TEMP || os.tmpdir(),
|
|
TMP: process.env.TMP || os.tmpdir(),
|
|
},
|
|
});
|
|
}
|
|
|
|
function parseJson(result) {
|
|
return JSON.parse(result.stdout || '{}');
|
|
}
|
|
|
|
function withRuntimeConfig(portal, config, fn) {
|
|
const filePath = path.join(distRoot, portal, 'runtime-config.json');
|
|
const existed = fs.existsSync(filePath);
|
|
const previous = existed ? fs.readFileSync(filePath, 'utf8') : '';
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
fs.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
|
|
try {
|
|
return fn(filePath);
|
|
} finally {
|
|
if (existed) fs.writeFileSync(filePath, previous, 'utf8');
|
|
else fs.rmSync(filePath, { force: true });
|
|
}
|
|
}
|
|
|
|
const loose = run();
|
|
assert.equal(loose.status, 0, loose.stderr);
|
|
const loosePayload = parseJson(loose);
|
|
assert.equal(loosePayload.schemaVersion, 1);
|
|
assert.equal(loosePayload.summary.portals, 3);
|
|
assert.equal(loosePayload.portals.length, 3);
|
|
assert.ok(loosePayload.portals.every(item => item.buildCommand.startsWith('npm run build:taro:h5:')));
|
|
assert.ok(loosePayload.portals.filter(item => item.dist.exists).every(item => /^[0-9a-f]{64}$/.test(item.dist.treeSha256)));
|
|
assert.ok(loosePayload.checks.some(item => item.id === 'build.student.portal_env' && item.status === 'pass'));
|
|
|
|
const strictMissingRuntime = run(['--require-runtime-config']);
|
|
assert.notEqual(strictMissingRuntime.status, 0, 'strict manifest should fail when runtime-config files are missing');
|
|
const strictPayload = parseJson(strictMissingRuntime);
|
|
assert.ok(
|
|
strictPayload.checks.some(item => item.id === 'runtime.student.exists' && item.status === 'fail'),
|
|
'missing student runtime config should be a failure',
|
|
);
|
|
|
|
const validConfig = {
|
|
portal: 'student',
|
|
apiBaseUrl: 'https://api.example.com',
|
|
supabaseUrl: 'https://auth.example.com',
|
|
supabasePublishableKey: 'sb_publishable_test_key',
|
|
tenantCode: '',
|
|
};
|
|
|
|
withRuntimeConfig('h5-student', validConfig, () => {
|
|
const withStudentRuntime = run();
|
|
assert.equal(withStudentRuntime.status, 0, withStudentRuntime.stderr);
|
|
const payload = parseJson(withStudentRuntime);
|
|
const student = payload.portals.find(item => item.portal === 'student');
|
|
assert.equal(student.runtimeConfig.exists, true);
|
|
assert.equal(student.runtimeConfig.tenantCodeMode, 'domain-resolved');
|
|
assert.ok(payload.checks.some(item => item.id === 'runtime.student.api_https' && item.status === 'pass'));
|
|
});
|
|
|
|
withRuntimeConfig('h5-student', {
|
|
...validConfig,
|
|
supabasePublishableKey: 'replace-with-supabase-publishable-key',
|
|
}, () => {
|
|
const placeholder = run();
|
|
assert.notEqual(placeholder.status, 0, 'production manifest should reject placeholder publishable key in dist runtime config');
|
|
const payload = parseJson(placeholder);
|
|
assert.ok(payload.checks.some(item => item.id === 'runtime.student.publishable_key' && item.status === 'fail'));
|
|
});
|
|
|
|
withRuntimeConfig('h5-student', {
|
|
...validConfig,
|
|
DATABASE_URL: `postgres${'ql'}://postgres:postgres@example.com/postgres`,
|
|
}, () => {
|
|
const leakedSecret = run();
|
|
assert.notEqual(leakedSecret.status, 0, 'manifest should reject forbidden runtime config keys');
|
|
const payload = parseJson(leakedSecret);
|
|
assert.ok(payload.checks.some(item => item.id === 'runtime.student.keys' && item.status === 'fail'));
|
|
assert.ok(payload.checks.some(item => item.id === 'runtime.student.secrets' && item.status === 'fail'));
|
|
});
|
|
|
|
const outputPath = path.join(os.tmpdir(), `taro-release-manifest-${Date.now()}.json`);
|
|
const written = run(['--write', outputPath]);
|
|
assert.equal(written.status, 0, written.stderr);
|
|
assert.equal(fs.existsSync(outputPath), true);
|
|
const writtenPayload = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
|
assert.equal(writtenPayload.schemaVersion, 1);
|
|
fs.rmSync(outputPath, { force: true });
|
|
|
|
console.log('[PASS] Taro H5 release manifest');
|