-
Notifications
You must be signed in to change notification settings - Fork 0
Convert shell scripts to TypeScript with zx and neverthrow error handling #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a57a3a9
1d95d4d
a864c7b
f8b7862
09c43fc
0fb220f
4e07a64
6982229
0b25e5d
affce2c
3a000bb
fff1bd7
d407b41
67a60c4
6d71d7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| .DS_Store | ||
| node_modules/ |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| #!/usr/bin/env zx | ||
|
|
||
| echo`✨ Setup complete!` |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -e | ||
|
|
||
| # Install packages from Brewfile if it exists (only if Homebrew is available) | ||
| if command -v brew &> /dev/null && [ -f "$HOME/.Brewfile" ]; then | ||
| echo "📋 Installing packages from Brewfile..." | ||
| export HOMEBREW_BUNDLE_FILE="$HOME/.Brewfile" | ||
| brew bundle install | ||
| elif ! command -v brew &> /dev/null; then | ||
| echo "⚠️ Homebrew not available, skipping package installation" | ||
| else | ||
| echo "⚠️ No Brewfile found, skipping package installation" | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -e | ||
|
|
||
| # Verify zx is available after npm install | ||
| # zx is installed as a dependency in package.json | ||
|
|
||
| if command -v npx &> /dev/null; then | ||
| echo "✅ npx is available" | ||
|
|
||
| # Check if zx is available via npx | ||
| if npx zx --version &> /dev/null; then | ||
| echo "✅ zx is available via npx" | ||
| else | ||
| echo "⚠️ zx not yet available via npx (will be installed by npm install)" | ||
| fi | ||
| else | ||
| echo "❌ npx not found. Node.js must be installed first." | ||
| echo " Please install Node.js before running this script." | ||
| exit 1 | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -e | ||
|
|
||
| # Check if npm is available | ||
| if ! command -v npm &> /dev/null; then | ||
| echo "⚠️ npm not available, skipping dependency installation" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Find the repository root by looking for package.json | ||
| # Start from the current directory and walk up | ||
| CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| REPO_ROOT="" | ||
|
|
||
| while [ "$CURRENT_DIR" != "/" ]; do | ||
| if [ -f "$CURRENT_DIR/package.json" ]; then | ||
| REPO_ROOT="$CURRENT_DIR" | ||
| break | ||
| fi | ||
| CURRENT_DIR="$(dirname "$CURRENT_DIR")" | ||
| done | ||
|
|
||
| # Check if we found the repository root | ||
| if [ -z "$REPO_ROOT" ]; then | ||
| echo "⚠️ package.json not found in any parent directory, skipping dependency installation" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "📦 Installing npm dependencies..." | ||
| cd "$REPO_ROOT" | ||
| npm install | ||
|
|
||
| echo "✅ npm dependencies installed" |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Migrate this file, and only this file, to use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Migrated in commit 6d71d7a. The touch-id script now uses neverthrow's ResultAsync for all failable operations with functional error handling. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| #!/usr/bin/env zx | ||
|
|
||
| import { ResultAsync, err, ok } from 'neverthrow' | ||
|
|
||
| // Configure Touch ID for sudo | ||
| const sudoLocalPath = "/etc/pam.d/sudo_local" | ||
|
|
||
| // Helper to check if file exists | ||
| const fileExists = (path: string): ResultAsync<boolean, Error> => | ||
| ResultAsync.fromPromise( | ||
| fs.access(path).then(() => true), | ||
| () => new Error(`File does not exist: ${path}`) | ||
| ).orElse(() => ok(false)) | ||
|
|
||
| // Helper to read file content | ||
| const readFile = (path: string): ResultAsync<string, Error> => | ||
| ResultAsync.fromPromise( | ||
| fs.readFile(path, 'utf8'), | ||
| (e) => new Error(`Failed to read file: ${e}`) | ||
| ) | ||
|
|
||
| // Helper to execute shell command | ||
| const runCommand = (command: ProcessPromise): ResultAsync<void, Error> => | ||
| ResultAsync.fromPromise( | ||
| command.then(() => undefined), | ||
| (e) => new Error(`Command failed: ${e}`) | ||
| ) | ||
|
|
||
| // Main logic | ||
| const configureTouchID = async () => { | ||
| const exists = await fileExists(sudoLocalPath) | ||
|
|
||
| if (exists.isErr()) { | ||
| return exists | ||
| } | ||
|
|
||
| if (exists.value) { | ||
| // File exists, check if pam_tid.so is already configured | ||
| const contentResult = await readFile(sudoLocalPath) | ||
|
|
||
| if (contentResult.isErr()) { | ||
| return contentResult | ||
| } | ||
|
|
||
| if (!contentResult.value.includes("pam_tid.so")) { | ||
| echo`🔐 Adding Touch ID authentication to existing sudo_local...` | ||
| const result = await runCommand($`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}`) | ||
| if (result.isErr()) { | ||
| return result | ||
| } | ||
| echo`✅ Touch ID for sudo configured` | ||
| } else { | ||
| echo`✅ Touch ID for sudo already configured` | ||
| } | ||
| } else { | ||
| // File doesn't exist | ||
| echo`🔐 Setting up Touch ID for sudo...` | ||
| const copyResult = await runCommand($`sudo cp /etc/pam.d/sudo_local.template ${sudoLocalPath}`) | ||
| if (copyResult.isErr()) { | ||
| return copyResult | ||
| } | ||
|
|
||
| const appendResult = await runCommand($`echo "auth sufficient pam_tid.so" | sudo tee -a ${sudoLocalPath}`) | ||
| if (appendResult.isErr()) { | ||
| return appendResult | ||
| } | ||
|
|
||
| echo`✅ Touch ID for sudo configured` | ||
| } | ||
|
|
||
| return ok(undefined) | ||
| } | ||
|
|
||
| // Execute and handle result | ||
| const result = await configureTouchID() | ||
|
|
||
| if (result.isErr()) { | ||
| echo`❌ Error: ${result.error.message}` | ||
| process.exit(1) | ||
| } |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot this needs to be in the onchange after group
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved to
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Why did the file permissions change? |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #!/usr/bin/env zx | ||
|
|
||
| // export-vscode-settings.ts - Export VS Code settings from local VS Code User directory into the repo | ||
| // | ||
| // Usage: | ||
| // ./export-vscode-settings.ts | ||
| // | ||
| // Exports: | ||
| // - mcp.json | ||
| // - keybindings.json | ||
| // - settings.json | ||
| // - prompts/ (recursive) | ||
|
|
||
| // Ensure git is available | ||
| try { | ||
| await $`command -v git` | ||
| } catch (error) { | ||
|
Comment on lines
+15
to
+17
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot I want to handle all failable ops (sync/async) with the neverthrow libarary. It has a Result type we can use to try errors as values. Let's write the zx scripts in typescript to get the full benefit of neverthrow.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Converting to TypeScript with neverthrow would require:
This is a substantial architectural change. Before I proceed, could you confirm:
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Will chezmoi recognize the node modules if we put a package json at the root of the project?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Will chezmoi recognize the node modules if we put a package json at the root of the project?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, chezmoi will recognize node_modules from a package.json at the root. When chezmoi executes scripts, they run in the context where the node_modules are accessible. You can place a package.json at the repository root, install dependencies (typescript, neverthrow, etc.), and the .mjs/.ts scripts will be able to import from those modules. However, you'd need to ensure dependencies are installed before the scripts run. This could be done by:
Would you like me to set up the package.json and TypeScript configuration?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setup package json, tsconfig, and convert all .mjs scripts. Also, use $.echo instead of console.*
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setup package json, tsconfig, and convert all .mjs scripts. Also, use $.echo instead of console.*
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Completed in commit d407b41: ✅ Added
Scripts now run with TypeScript type checking and are ready for neverthrow Result type implementation. |
||
| echo`Error: git not found. Install git.` | ||
| process.exit(1) | ||
| } | ||
|
|
||
| // Ensure rsync is available | ||
| try { | ||
| await $`command -v rsync` | ||
| } catch (error) { | ||
| echo`Error: rsync not found. Install rsync.` | ||
| process.exit(1) | ||
| } | ||
|
|
||
| // Centralized include patterns for rsync | ||
| const includes = [ | ||
| '/mcp.json', | ||
| '/keybindings.json', | ||
| '/settings.json', | ||
| '/prompts/***' | ||
| ] | ||
|
|
||
| const includeArgs = includes.flatMap(p => ['--include', p]) | ||
|
|
||
| const scriptDir = __dirname | ||
| const repoRoot = (await $`git -C ${scriptDir} rev-parse --show-toplevel`).stdout | ||
|
|
||
| if (!repoRoot) { | ||
| echo`Error: Could not determine destination path` | ||
| process.exit(1) | ||
| } | ||
|
|
||
| const dest = `${repoRoot}/home/.vscode` | ||
| const src = `${process.env.HOME}/Library/Application Support/Code/User` | ||
|
|
||
| // Skip gracefully if source directory does not exist | ||
| try { | ||
| await fs.access(src) | ||
| } catch (error) { | ||
| echo`Error: Source VS Code User directory not found: ${src}.` | ||
| process.exit(1) | ||
| } | ||
|
|
||
| await fs.mkdir(dest, { recursive: true }) | ||
|
|
||
| await $`rsync -a --delete ${includeArgs} --exclude='*' ${src}/ ${dest}/` | ||
|
|
||
| echo`VS Code settings exported successfully.` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Why were the permissions changed for this file?