Merge branch 'main' into setup-scratch-agent-cleanup

This commit is contained in:
gavrielc
2026-04-30 23:20:32 +03:00
committed by GitHub
3 changed files with 66 additions and 16 deletions

View File

@@ -85,17 +85,21 @@ async function main(): Promise<void> {
// Welcome menu — default path or open advanced overrides before any setup
// work begins. Default lands on standard so Enter is the happy path.
const startChoice = ensureAnswer(
await brightSelect<'default' | 'advanced'>({
message: 'How would you like to begin?',
options: [
{ value: 'default', label: 'Standard setup' },
{ value: 'advanced', label: 'Advanced', hint: 'override defaults' },
],
initialValue: 'default',
}),
) as 'default' | 'advanced';
setupLog.userInput('start_choice', startChoice);
// On sg re-exec, the user already chose — skip straight to standard.
let startChoice: 'default' | 'advanced' = 'default';
if (process.env.NANOCLAW_REEXEC_SG !== '1') {
startChoice = ensureAnswer(
await brightSelect<'default' | 'advanced'>({
message: 'How would you like to begin?',
options: [
{ value: 'default', label: 'Standard setup' },
{ value: 'advanced', label: 'Advanced', hint: 'override defaults' },
],
initialValue: 'default',
}),
) as 'default' | 'advanced';
setupLog.userInput('start_choice', startChoice);
}
if (startChoice === 'advanced') {
configValues = await runAdvancedScreen(configValues);
applyToEnv(configValues);
@@ -1147,9 +1151,11 @@ function maybeReexecUnderSg(): void {
if (spawnSync('which', ['sg'], { stdio: 'ignore' }).status !== 0) return;
p.log.warn(brandBody('Docker socket not accessible in current group. Re-executing under `sg docker`.'));
const existingSkip = (process.env.NANOCLAW_SKIP ?? '').split(',').map((s) => s.trim()).filter(Boolean);
const skipList = [...new Set([...existingSkip, ...setupLog.completedStepNames()])].join(',');
const res = spawnSync('sg', ['docker', '-c', 'pnpm run setup:auto'], {
stdio: 'inherit',
env: { ...process.env, NANOCLAW_REEXEC_SG: '1' },
env: { ...process.env, NANOCLAW_REEXEC_SG: '1', ...(skipList ? { NANOCLAW_SKIP: skipList } : {}) },
});
process.exit(res.status ?? 1);
}

View File

@@ -127,11 +127,22 @@ export async function run(args: string[]): Promise<void> {
}
// Socket is unreachable due to group perms — current shell's supplementary
// groups are fixed at login, so `usermod -aG docker` (via install-docker.sh
// or a prior install) doesn't affect us until next login. Re-exec this
// step under `sg docker` so the child picks up docker as its primary
// group and can talk to /var/run/docker.sock without a logout.
// groups are fixed at login, so `usermod -aG docker` doesn't affect us
// until next login. Ensure the user is in the docker group (install-docker.sh
// does this on fresh installs, but skips when Docker is already present),
// then re-exec under `sg docker` so the child picks up docker as its
// primary group and can talk to /var/run/docker.sock without a logout.
if (status === 'no-permission' && getPlatform() === 'linux' && commandExists('sg')) {
// Ensure the current user is in the docker group — without this,
// sg will ask for the (typically unset) group password and fail.
const inGroup = spawnSync('id', ['-nG'], { encoding: 'utf-8' });
if (!(inGroup.stdout ?? '').split(/\s+/).includes('docker')) {
log.info('Adding current user to docker group');
spawnSync('sudo', ['usermod', '-aG', 'docker', process.env.USER ?? ''], {
stdio: 'inherit',
});
}
log.info('Re-executing container step under `sg docker`');
const res = spawnSync(
'sg',