fix(preflight): default mounts config and flag root-only steps
This commit is contained in:
parent
cfd327f285
commit
f1f5cf7947
2 changed files with 81 additions and 19 deletions
|
|
@ -24,6 +24,26 @@ function parseArgs(args: string[]): { empty: boolean; json: string } {
|
|||
return { empty, json };
|
||||
}
|
||||
|
||||
function writeAllowlist(
|
||||
configFile: string,
|
||||
parsed: { allowedRoots?: unknown[]; blockedPatterns?: unknown[]; nonMainReadOnly?: boolean },
|
||||
): { allowedRoots: number; nonMainReadOnly: string } {
|
||||
fs.writeFileSync(configFile, JSON.stringify(parsed, null, 2) + '\n');
|
||||
return {
|
||||
allowedRoots: Array.isArray(parsed.allowedRoots) ? parsed.allowedRoots.length : 0,
|
||||
nonMainReadOnly: parsed.nonMainReadOnly === false ? 'false' : 'true',
|
||||
};
|
||||
}
|
||||
|
||||
function writeEmptyAllowlist(configFile: string): { allowedRoots: number; nonMainReadOnly: string } {
|
||||
logger.info('Writing empty mount allowlist');
|
||||
return writeAllowlist(configFile, {
|
||||
allowedRoots: [],
|
||||
blockedPatterns: [],
|
||||
nonMainReadOnly: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function run(args: string[]): Promise<void> {
|
||||
const { empty, json } = parseArgs(args);
|
||||
const homeDir = os.homedir();
|
||||
|
|
@ -42,13 +62,7 @@ export async function run(args: string[]): Promise<void> {
|
|||
let nonMainReadOnly = 'true';
|
||||
|
||||
if (empty) {
|
||||
logger.info('Writing empty mount allowlist');
|
||||
const emptyConfig = {
|
||||
allowedRoots: [],
|
||||
blockedPatterns: [],
|
||||
nonMainReadOnly: true,
|
||||
};
|
||||
fs.writeFileSync(configFile, JSON.stringify(emptyConfig, null, 2) + '\n');
|
||||
({ allowedRoots, nonMainReadOnly } = writeEmptyAllowlist(configFile));
|
||||
} else if (json) {
|
||||
// Validate JSON with JSON.parse (not piped through shell)
|
||||
let parsed: { allowedRoots?: unknown[]; nonMainReadOnly?: boolean };
|
||||
|
|
@ -68,15 +82,38 @@ export async function run(args: string[]): Promise<void> {
|
|||
return; // unreachable but satisfies TS
|
||||
}
|
||||
|
||||
fs.writeFileSync(configFile, JSON.stringify(parsed, null, 2) + '\n');
|
||||
allowedRoots = Array.isArray(parsed.allowedRoots)
|
||||
? parsed.allowedRoots.length
|
||||
: 0;
|
||||
nonMainReadOnly = parsed.nonMainReadOnly === false ? 'false' : 'true';
|
||||
({ allowedRoots, nonMainReadOnly } = writeAllowlist(configFile, parsed));
|
||||
} else {
|
||||
// Read from stdin
|
||||
// Read from stdin when piped, otherwise create the default empty config.
|
||||
if (process.stdin.isTTY) {
|
||||
({ allowedRoots, nonMainReadOnly } = writeEmptyAllowlist(configFile));
|
||||
logger.info('No stdin provided, wrote default empty mount allowlist');
|
||||
emitStatus('CONFIGURE_MOUNTS', {
|
||||
PATH: configFile,
|
||||
ALLOWED_ROOTS: allowedRoots,
|
||||
NON_MAIN_READ_ONLY: nonMainReadOnly,
|
||||
DEFAULTED: 'yes',
|
||||
STATUS: 'success',
|
||||
LOG: 'logs/setup.log',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Reading mount allowlist from stdin');
|
||||
const input = fs.readFileSync(0, 'utf-8');
|
||||
if (!input.trim()) {
|
||||
({ allowedRoots, nonMainReadOnly } = writeEmptyAllowlist(configFile));
|
||||
logger.info('Empty stdin, wrote default empty mount allowlist');
|
||||
emitStatus('CONFIGURE_MOUNTS', {
|
||||
PATH: configFile,
|
||||
ALLOWED_ROOTS: allowedRoots,
|
||||
NON_MAIN_READ_ONLY: nonMainReadOnly,
|
||||
DEFAULTED: 'yes',
|
||||
STATUS: 'success',
|
||||
LOG: 'logs/setup.log',
|
||||
});
|
||||
return;
|
||||
}
|
||||
let parsed: { allowedRoots?: unknown[]; nonMainReadOnly?: boolean };
|
||||
try {
|
||||
parsed = JSON.parse(input);
|
||||
|
|
@ -94,11 +131,7 @@ export async function run(args: string[]): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
fs.writeFileSync(configFile, JSON.stringify(parsed, null, 2) + '\n');
|
||||
allowedRoots = Array.isArray(parsed.allowedRoots)
|
||||
? parsed.allowedRoots.length
|
||||
: 0;
|
||||
nonMainReadOnly = parsed.nonMainReadOnly === false ? 'false' : 'true';
|
||||
({ allowedRoots, nonMainReadOnly } = writeAllowlist(configFile, parsed));
|
||||
}
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { spawnSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
import { AGENT_DOMAIN, AGENT_NAME } from '../src/config.js';
|
||||
import { logger } from '../src/logger.js';
|
||||
|
|
@ -18,6 +19,7 @@ interface StepDefinition {
|
|||
command: string;
|
||||
args: string[];
|
||||
interactive?: boolean;
|
||||
requiresRoot?: boolean;
|
||||
}
|
||||
|
||||
interface StepResult {
|
||||
|
|
@ -160,36 +162,41 @@ function buildSteps(opts: PreflightArgs): StepDefinition[] {
|
|||
label: 'Jails',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'jails', '--create'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'db',
|
||||
label: 'DB',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'db'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'git',
|
||||
label: 'Git',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'git'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'cms',
|
||||
label: 'CMS',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'cms'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'hosts',
|
||||
label: 'Hosts',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'hosts'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'mounts',
|
||||
label: 'Mounts',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'mounts'],
|
||||
args: ['run', 'setup', '--', '--step', 'mounts', '--empty'],
|
||||
},
|
||||
{
|
||||
id: 'telegram-auth',
|
||||
|
|
@ -208,6 +215,7 @@ function buildSteps(opts: PreflightArgs): StepDefinition[] {
|
|||
label: 'Verify',
|
||||
command: 'npm',
|
||||
args: ['run', 'setup', '--', '--step', 'verify'],
|
||||
requiresRoot: true,
|
||||
},
|
||||
{
|
||||
id: 'doctor',
|
||||
|
|
@ -229,6 +237,27 @@ function runStep(
|
|||
const commandLine = [step.command, ...step.args].map(quoteShell).join(' ');
|
||||
const logFile = path.join(runDir, `${step.id}.log`);
|
||||
const interactive = step.interactive === true;
|
||||
const freebsdNeedsRoot = os.platform() === 'freebsd' && process.getuid?.() !== 0;
|
||||
|
||||
if (step.requiresRoot && freebsdNeedsRoot) {
|
||||
const output = 'root_required\n';
|
||||
fs.writeFileSync(logFile, output);
|
||||
const finished = new Date();
|
||||
return {
|
||||
id: step.id,
|
||||
label: step.label,
|
||||
commandLine,
|
||||
exitCode: 1,
|
||||
status: 'failed',
|
||||
startedAt: started.toISOString(),
|
||||
finishedAt: finished.toISOString(),
|
||||
logFile,
|
||||
fields: {
|
||||
ERROR: 'root_required',
|
||||
HINT: 'rerun_root_required_steps_as_root',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (interactive && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
||||
const output = 'interactive_tty_required\n';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue