chore(ql3): add opt-in deployment diagnostics

This commit is contained in:
whyour
2026-09-02 07:54:30 +08:00
parent f76e5f16da
commit a8ca739fe8
4 changed files with 48 additions and 0 deletions
+1
View File
@@ -715,6 +715,7 @@ jobs:
HEADLESS_IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} HEADLESS_IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }}
CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }}
OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }}
QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS: '1'
TRIAL_VARIANT: ${{ inputs.local_alpha_variant }} TRIAL_VARIANT: ${{ inputs.local_alpha_variant }}
run: | run: |
set -euo pipefail set -euo pipefail
@@ -10,6 +10,7 @@ import {
consumeLocalServiceManagerCutoverOutcomeCommandFile, consumeLocalServiceManagerCutoverOutcomeCommandFile,
consumeLocalServiceManagerLegacyRollbackCommandFile, consumeLocalServiceManagerLegacyRollbackCommandFile,
inspectLocalDeploymentStatusCommandFile, inspectLocalDeploymentStatusCommandFile,
LocalDeploymentConfigurationError,
preflightLocalDeploymentComposeCommandFile, preflightLocalDeploymentComposeCommandFile,
prepareLocalServiceManagerIntentCommandFile, prepareLocalServiceManagerIntentCommandFile,
prepareLocalServiceManagerLegacyRollbackCommandFile, prepareLocalServiceManagerLegacyRollbackCommandFile,
@@ -282,8 +283,15 @@ async function main(argv: readonly string[]): Promise<void> {
} catch (error) { } catch (error) {
const candidate = error as { const candidate = error as {
readonly code?: unknown; readonly code?: unknown;
readonly message?: unknown;
readonly name?: unknown; readonly name?: unknown;
}; };
const diagnosticMessage =
process.env.QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS === '1' &&
error instanceof LocalDeploymentConfigurationError &&
typeof candidate.message === 'string'
? { message: candidate.message }
: {};
process.stderr.write( process.stderr.write(
`${JSON.stringify({ `${JSON.stringify({
code: code:
@@ -291,6 +299,7 @@ async function main(argv: readonly string[]): Promise<void> {
? candidate.code ? candidate.code
: 'QL3_LOCAL_DEPLOYMENT_FAILED', : 'QL3_LOCAL_DEPLOYMENT_FAILED',
name: typeof candidate.name === 'string' ? candidate.name : 'Error', name: typeof candidate.name === 'string' ? candidate.name : 'Error',
...diagnosticMessage,
})}\n`, })}\n`,
); );
process.exitCode = 1; process.exitCode = 1;
@@ -1000,6 +1000,42 @@ test('status CLI is private-command-only and emits no deployment authority', asy
assert.equal(rejected.stderr.includes(state.deploymentRoot), false); assert.equal(rejected.stderr.includes(state.deploymentRoot), false);
}); });
test('deployment CLI exposes configuration detail only under explicit diagnostics', (t) => {
const state = fixture(t, 'compose');
const commandPath = path.join(state.managementRoot, 'invalid-command.json');
fs.writeFileSync(commandPath, '{}\n', { mode: 0o600 });
const cli = path.resolve(
__dirname,
'../dist/deployment/localDeploymentCli.js',
);
const normal = spawnSync(
process.execPath,
[cli, 'prepare', '--command-file', commandPath],
{ encoding: 'utf8' },
);
assert.equal(normal.status, 1);
assert.deepEqual(JSON.parse(normal.stderr), {
code: 'QL3_LOCAL_DEPLOYMENT_CONFIGURATION_INVALID',
name: 'LocalDeploymentConfigurationError',
});
const diagnostic = spawnSync(
process.execPath,
[cli, 'prepare', '--command-file', commandPath],
{
encoding: 'utf8',
env: { ...process.env, QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS: '1' },
},
);
assert.equal(diagnostic.status, 1);
assert.deepEqual(JSON.parse(diagnostic.stderr), {
code: 'QL3_LOCAL_DEPLOYMENT_CONFIGURATION_INVALID',
name: 'LocalDeploymentConfigurationError',
message:
'Local deployment configuration is invalid: command shape is invalid',
});
});
test('durable status keeps live supervisor and database authority out of its module', () => { test('durable status keeps live supervisor and database authority out of its module', () => {
const source = fs.readFileSync( const source = fs.readFileSync(
path.resolve(__dirname, '../src/deployment/localDeploymentStatus.ts'), path.resolve(__dirname, '../src/deployment/localDeploymentStatus.ts'),
@@ -210,6 +210,8 @@ run_deploy() {
--mount "type=bind,src=$decision_parent,dst=$decision_parent,readonly" --mount "type=bind,src=$decision_parent,dst=$decision_parent,readonly"
[ -z "$secondary_decision_parent" ] || set -- "$@" \ [ -z "$secondary_decision_parent" ] || set -- "$@" \
--mount "type=bind,src=$secondary_decision_parent,dst=$secondary_decision_parent,readonly" --mount "type=bind,src=$secondary_decision_parent,dst=$secondary_decision_parent,readonly"
[ "${QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS:-0}" != 1 ] || set -- "$@" \
--env QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS=1
set -- "$@" "$OPERATOR_IMAGE" deploy "$subcommand" \ set -- "$@" "$OPERATOR_IMAGE" deploy "$subcommand" \
--command-file "$command_root/$command_file" --command-file "$command_root/$command_file"
result_stage="$result_root/.$result_file.$$" result_stage="$result_root/.$result_file.$$"