Skip to content

Commit 6f14916

Browse files
committed
fix: remove duplicate custom_script and dotfiles from install script
The CLI already handles post-install scripts (with confirmation) and dotfiles (clone/stow) via stepPostInstall and stepDotfiles. The install script was executing them a second time unconditionally. Now the install script only bootstraps Xcode + Homebrew + openboot, then delegates everything to the CLI.
1 parent 1938390 commit 6f14916

4 files changed

Lines changed: 20 additions & 142 deletions

File tree

src/hooks.server.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const handle: Handle = async ({ event, resolve }) => {
6767
}));
6868
}
6969

70-
const script = generateInstallScript(config.username, config.slug, config.custom_script, config.dotfiles_repo || '');
70+
const script = generateInstallScript(config.username, config.slug);
7171

7272
env.DB.prepare('UPDATE configs SET install_count = install_count + 1 WHERE alias = ?').bind(alias).run().catch((e: unknown) => console.error('install count update failed:', e));
7373

@@ -92,8 +92,8 @@ export const handle: Handle = async ({ event, resolve }) => {
9292
const slug = installShMatch[2];
9393

9494
const config = await env.DB.prepare(
95-
'SELECT c.custom_script, c.visibility, c.dotfiles_repo, c.user_id FROM configs c JOIN users u ON c.user_id = u.id WHERE u.username = ? AND c.slug = ?'
96-
).bind(username, slug).first<{ custom_script: string; visibility: string; dotfiles_repo: string; user_id: string }>();
95+
'SELECT c.visibility, c.user_id FROM configs c JOIN users u ON c.user_id = u.id WHERE u.username = ? AND c.slug = ?'
96+
).bind(username, slug).first<{ visibility: string; user_id: string }>();
9797

9898
if (config) {
9999
if (config.visibility === 'private') {
@@ -103,7 +103,7 @@ export const handle: Handle = async ({ event, resolve }) => {
103103
}));
104104
}
105105

106-
const script = generateInstallScript(username, slug, config.custom_script, config.dotfiles_repo || '');
106+
const script = generateInstallScript(username, slug);
107107

108108
env.DB.prepare('UPDATE configs SET install_count = install_count + 1 WHERE user_id = ? AND slug = ?').bind(config.user_id, slug).run().catch((e: unknown) => console.error('install count update failed:', e));
109109

@@ -139,7 +139,7 @@ export const handle: Handle = async ({ event, resolve }) => {
139139
}));
140140
}
141141

142-
const script = generateInstallScript(username, slug, config.custom_script, config.dotfiles_repo || '');
142+
const script = generateInstallScript(username, slug);
143143

144144
env.DB.prepare('UPDATE configs SET install_count = install_count + 1 WHERE user_id = ? AND slug = ?').bind(config.user_id, slug).run().catch((e: unknown) => console.error('install count update failed:', e));
145145

src/lib/server/install-script.test.ts

Lines changed: 11 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,20 @@ describe('generatePrivateInstallScript', () => {
118118

119119
describe('generateInstallScript', () => {
120120
it('should wrap everything in main() for safe curl|bash piping', () => {
121-
const script = generateInstallScript('testuser', 'my-config', '', '');
121+
const script = generateInstallScript('testuser', 'my-config');
122122

123123
expect(script).toContain('main()');
124124
expect(script).toMatch(/main "\$@"\s*$/);
125125
expect(script).toContain('exec < /dev/tty');
126126

127127
// CRITICAL: main() must never return after exec < /dev/tty redirects stdin.
128-
// Every code path inside main() must end with exit or exec, otherwise
129-
// bash hangs waiting on /dev/tty when run via "curl | bash".
130128
expect(script).toContain('exit 0\n}');
131129
// No code should appear between main "$@" and end of script
132130
expect(script).not.toMatch(/main "\$@"[\s\S]*exit/);
133131
});
134132

135-
it('should generate basic install script without custom content', () => {
136-
const script = generateInstallScript('testuser', 'my-config', '', '');
133+
it('should generate basic install script', () => {
134+
const script = generateInstallScript('testuser', 'my-config');
137135

138136
expect(script).toContain('#!/bin/bash');
139137
expect(script).toContain('OpenBoot Installer');
@@ -143,117 +141,49 @@ describe('generateInstallScript', () => {
143141
});
144142

145143
it('should include Xcode CLT installation', () => {
146-
const script = generateInstallScript('testuser', 'my-config', '', '');
144+
const script = generateInstallScript('testuser', 'my-config');
147145

148146
expect(script).toContain('install_xcode_clt()');
149147
expect(script).toContain('xcode-select -p');
150148
expect(script).toContain('xcode-select --install');
151149
});
152150

153151
it('should include Homebrew installation', () => {
154-
const script = generateInstallScript('testuser', 'my-config', '', '');
152+
const script = generateInstallScript('testuser', 'my-config');
155153

156154
expect(script).toContain('install_homebrew()');
157155
expect(script).toContain('command -v brew');
158156
expect(script).toContain('Homebrew/install/HEAD/install.sh');
159157
});
160158

161159
it('should handle ARM64 architecture', () => {
162-
const script = generateInstallScript('testuser', 'my-config', '', '');
160+
const script = generateInstallScript('testuser', 'my-config');
163161

164162
expect(script).toContain('detect_arch()');
165163
expect(script).toContain('/opt/homebrew/bin/brew');
166164
expect(script).toContain('arm64)');
167165
});
168166

169-
it('should include custom script when provided', () => {
170-
const customScript = 'mkdir -p ~/projects\necho "Setup complete"';
171-
const script = generateInstallScript('testuser', 'my-config', customScript, '');
172-
173-
expect(script).toContain('Running Custom Post-Install Script');
174-
expect(script).toContain('base64 -d | bash');
175-
expect(script).toContain('CUSTOM_SCRIPT_EXIT=$?');
176-
});
177-
178-
it('should handle custom script errors gracefully', () => {
179-
const customScript = 'exit 1';
180-
const script = generateInstallScript('testuser', 'my-config', customScript, '');
181-
182-
expect(script).toContain('set +e');
183-
expect(script).toContain('if [ $CUSTOM_SCRIPT_EXIT -ne 0 ]');
184-
expect(script).toContain('Custom script exited with code');
185-
expect(script).toContain('Installation will continue');
186-
});
187-
188-
it('should not include custom script section when empty', () => {
189-
const script = generateInstallScript('testuser', 'my-config', '', '');
167+
it('should not include custom script or dotfiles sections', () => {
168+
const script = generateInstallScript('testuser', 'my-config');
190169

191170
expect(script).not.toContain('Running Custom Post-Install Script');
192171
expect(script).not.toContain('base64 -d');
193-
});
194-
195-
it('should include dotfiles setup when repo provided', () => {
196-
const dotfilesRepo = 'https://github.com/testuser/dotfiles.git';
197-
const script = generateInstallScript('testuser', 'my-config', '', dotfilesRepo);
198-
199-
expect(script).toContain('Setting up Dotfiles');
200-
expect(script).toContain('DOTFILES_REPO="https://github.com/testuser/dotfiles.git"');
201-
expect(script).toContain('DOTFILES_DIR="$HOME/.dotfiles"');
202-
expect(script).toContain('git clone "$DOTFILES_REPO"');
203-
expect(script).toContain('stow -v --target="$HOME"');
204-
});
205-
206-
it('should validate dotfiles repo URL is HTTPS', () => {
207-
const dotfilesRepo = 'https://github.com/testuser/dotfiles.git';
208-
const script = generateInstallScript('testuser', 'my-config', '', dotfilesRepo);
209-
210-
expect(script).toContain('if [[ ! "$DOTFILES_REPO" =~ ^https:// ]]');
211-
expect(script).toContain('Invalid dotfiles repo URL (must use HTTPS)');
212-
});
213-
214-
it('should handle existing dotfiles directory with git pull', () => {
215-
const dotfilesRepo = 'https://github.com/testuser/dotfiles.git';
216-
const script = generateInstallScript('testuser', 'my-config', '', dotfilesRepo);
217-
218-
expect(script).toContain('if [ -d "$DOTFILES_DIR" ]');
219-
expect(script).toContain('Pulling latest changes...');
220-
expect(script).toContain('git pull');
221-
});
222-
223-
it('should remove existing zshrc files before stow', () => {
224-
const dotfilesRepo = 'https://github.com/testuser/dotfiles.git';
225-
const script = generateInstallScript('testuser', 'my-config', '', dotfilesRepo);
226-
227-
expect(script).toContain('rm -f "$HOME/.zshrc" "$HOME/.zshrc.pre-oh-my-zsh"');
228-
});
229-
230-
it('should not include dotfiles section when repo not provided', () => {
231-
const script = generateInstallScript('testuser', 'my-config', '', '');
232-
233172
expect(script).not.toContain('Setting up Dotfiles');
234173
expect(script).not.toContain('DOTFILES_REPO');
235174
expect(script).not.toContain('stow');
236175
});
237176

238177
it('should sanitize username and slug in all references', () => {
239-
const script = generateInstallScript('user@test', 'my config!', '', '');
178+
const script = generateInstallScript('user@test', 'my config!');
240179

241180
expect(script).toContain('usertest/myconfig');
242181
expect(script).not.toContain('user@test');
243182
expect(script).not.toContain('my config!');
244183
});
245184

246-
it('should include both custom script and dotfiles when both provided', () => {
247-
const customScript = 'echo "Custom setup"';
248-
const dotfilesRepo = 'https://github.com/testuser/dotfiles.git';
249-
const script = generateInstallScript('testuser', 'my-config', customScript, dotfilesRepo);
250-
251-
expect(script).toContain('Running Custom Post-Install Script');
252-
expect(script).toContain('Setting up Dotfiles');
253-
});
254-
255185
it('should install openboot via Homebrew tap', () => {
256-
const script = generateInstallScript('testuser', 'my-config', '', '');
186+
const script = generateInstallScript('testuser', 'my-config');
257187

258188
expect(script).toContain('TAP_NAME="openbootdotdev/tap"');
259189
expect(script).toContain('brew install ${TAP_NAME}/openboot');
@@ -262,7 +192,7 @@ describe('generateInstallScript', () => {
262192
});
263193

264194
it('should pass through additional arguments to openboot', () => {
265-
const script = generateInstallScript('testuser', 'my-config', '', '');
195+
const script = generateInstallScript('testuser', 'my-config');
266196

267197
expect(script).toContain('openboot --user "testuser/my-config" "$@"');
268198
});

src/lib/server/install-script.ts

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ main "\$@"
9797

9898
export function generateInstallScript(
9999
username: string,
100-
slug: string,
101-
customScript: string,
102-
dotfilesRepo: string
100+
slug: string
103101
): string {
104102
const safeUsername = sanitizeShellArg(username);
105103
const safeSlug = sanitizeShellArg(slug);
@@ -246,56 +244,6 @@ echo "Starting OpenBoot setup with config: @${safeUsername}/${safeSlug}"
246244
echo ""
247245
openboot --user "${safeUsername}/${safeSlug}" "\$@"
248246
249-
${
250-
customScript
251-
? `
252-
echo ""
253-
echo "=== Running Custom Post-Install Script ==="
254-
set +e
255-
CUSTOM_SCRIPT_B64="${btoa(unescape(encodeURIComponent(customScript)))}"
256-
echo "\$CUSTOM_SCRIPT_B64" | base64 -d | bash
257-
CUSTOM_SCRIPT_EXIT=$?
258-
set -e
259-
if [ $CUSTOM_SCRIPT_EXIT -ne 0 ]; then
260-
echo ""
261-
echo "⚠ Custom script exited with code $CUSTOM_SCRIPT_EXIT"
262-
echo " Installation will continue, but check script output above."
263-
fi
264-
`
265-
: ''
266-
}
267-
${
268-
dotfilesRepo
269-
? `
270-
echo ""
271-
echo "=== Setting up Dotfiles ==="
272-
DOTFILES_REPO="${dotfilesRepo}"
273-
DOTFILES_DIR="\$HOME/.dotfiles"
274-
275-
if [[ ! "\$DOTFILES_REPO" =~ ^https:// ]]; then
276-
echo "Error: Invalid dotfiles repo URL (must use HTTPS)"
277-
exit 1
278-
fi
279-
280-
if [ -d "\$DOTFILES_DIR" ]; then
281-
echo "Dotfiles directory already exists at \$DOTFILES_DIR"
282-
echo "Pulling latest changes..."
283-
cd "\$DOTFILES_DIR" && git pull
284-
else
285-
echo "Cloning dotfiles from \$DOTFILES_REPO..."
286-
git clone "\$DOTFILES_REPO" "\$DOTFILES_DIR"
287-
fi
288-
289-
cd "\$DOTFILES_DIR"
290-
echo "Deploying dotfiles with stow..."
291-
rm -f "\$HOME/.zshrc" "\$HOME/.zshrc.pre-oh-my-zsh"
292-
for dir in */; do
293-
[ -d "\$dir" ] && stow -v --target="\$HOME" "\${dir%/}" 2>/dev/null || true
294-
done
295-
`
296-
: ''
297-
}
298-
299247
echo ""
300248
echo "Installation complete!"
301249
exit 0

src/routes/[username]/[slug]/install/+server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export const GET: RequestHandler = async ({ platform, params, request }) => {
1212
return new Response('User not found', { status: 404 });
1313
}
1414

15-
const config = await env.DB.prepare('SELECT custom_script, visibility, dotfiles_repo FROM configs WHERE user_id = ? AND slug = ?')
15+
const config = await env.DB.prepare('SELECT visibility FROM configs WHERE user_id = ? AND slug = ?')
1616
.bind(user.id, params.slug)
17-
.first<{ custom_script: string; visibility: string; dotfiles_repo: string }>();
17+
.first<{ visibility: string }>();
1818

1919
if (!config) {
2020
return new Response('Config not found', { status: 404 });
@@ -34,7 +34,7 @@ export const GET: RequestHandler = async ({ platform, params, request }) => {
3434
}
3535
}
3636

37-
const script = generateInstallScript(params.username, params.slug, config.custom_script, config.dotfiles_repo || '');
37+
const script = generateInstallScript(params.username, params.slug);
3838

3939
return new Response(script, {
4040
headers: {

0 commit comments

Comments
 (0)