Merge branch 'main' into setup-scratch-agent-cleanup
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user