pondorasti 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,17 +8,26 @@ Built with [Bun](https://bun.sh), [Yargs](https://yargs.js.org/) for command par
8
8
 
9
9
  - 🍺 **Homebrew Management**: Install, update, and manage Homebrew packages
10
10
  - 📂 **Smart Cloning**: Clone GitHub repos to organized `~/repos/<owner>/<repo>` structure
11
- - 📦 **Native Output**: Shows real brew output for transparency
12
- - 🚀 **Fast**: Powered by Bun for blazing-fast execution
11
+ - 📦 **Standalone Binary**: Compiles to a single executable with embedded Brewfile
12
+ - 🚀 **Fresh Machine Setup**: Bootstrap a new Mac with a single command
13
13
  - 🔧 **Extensible**: Easy to add new commands
14
14
 
15
- ## Prerequisites
15
+ ## Fresh Machine Setup
16
16
 
17
- - macOS (optimized for Apple Silicon)
18
- - [Bun](https://bun.sh) runtime
19
- - [GitHub CLI](https://cli.github.com/) (`gh`) for clone command
17
+ On a brand new Mac, download and run the standalone binary:
20
18
 
21
- ## Installation
19
+ ```bash
20
+ # Download the binary (Apple Silicon)
21
+ curl -fsSL https://github.com/pondorasti/pondorasti/releases/latest/download/pd-darwin-arm64 -o pd
22
+ chmod +x pd
23
+
24
+ # Run the full bootstrap (installs Homebrew, then all packages from Brewfile)
25
+ ./pd bootstrap
26
+ ```
27
+
28
+ The compiled binary includes the Brewfile embedded, so it works without any dependencies.
29
+
30
+ ## Installation (Development)
22
31
 
23
32
  ```bash
24
33
  # Install Bun if you haven't already
@@ -31,7 +40,7 @@ cd pondorasti
31
40
  # Install dependencies
32
41
  bun install
33
42
 
34
- # Run commands
43
+ # Run commands directly
35
44
  bun run packages/cli/src/index.ts <command>
36
45
 
37
46
  # Or install globally
@@ -45,6 +54,14 @@ pd <command>
45
54
 
46
55
  ### Commands
47
56
 
57
+ #### `bootstrap` - Bootstrap a fresh machine
58
+
59
+ Installs Homebrew and runs brew bundle (which installs everything including Bun):
60
+
61
+ ```bash
62
+ pd bootstrap
63
+ ```
64
+
48
65
  #### `clone` - Clone GitHub repositories
49
66
 
50
67
  Clones repositories to `~/repos/<owner>/<repo>` and opens a shell in the directory.
@@ -77,17 +94,29 @@ pd brew bundle
77
94
  --version, -v Show version
78
95
  ```
79
96
 
80
- ## Development
97
+ ## Building
98
+
99
+ ### Standalone Binary
100
+
101
+ The CLI compiles to a standalone executable that includes the Bun runtime and embedded Brewfile:
81
102
 
82
103
  ```bash
83
- # Run in development mode
84
- bun dev
104
+ cd packages/cli
105
+
106
+ # Build for current platform
107
+ bun run build
108
+ ```
85
109
 
110
+ The compiled binary is ~57MB and requires no dependencies to run.
111
+
112
+ ### Development
113
+
114
+ ```bash
86
115
  # Run tests
87
116
  bun test
88
117
 
89
- # Build for production
90
- bun build --compile --outfile=pondorasti src/index.ts
118
+ # Run in development mode
119
+ bun run packages/cli/src/index.ts <command>
91
120
  ```
92
121
 
93
122
  ## Architecture
@@ -98,31 +127,19 @@ packages/cli/
98
127
  │ ├── index.ts # CLI entry with yargs
99
128
  │ ├── commands/ # Command implementations
100
129
  │ │ ├── brew.ts # Homebrew management
101
- │ │ └── clone.ts # GitHub repo cloning
130
+ │ │ ├── clone.ts # GitHub repo cloning
131
+ │ │ └── bootstrap.ts # Fresh machine bootstrap
102
132
  │ ├── tools/ # External tool wrappers
103
- │ │ └── homebrew.ts # Homebrew operations
133
+ │ │ ├── homebrew.ts # Homebrew operations
134
+ │ │ └── bun.ts # Bun runtime operations
104
135
  │ └── utils/ # Utilities
136
+ │ ├── brewfile.ts # Brewfile embedding & extraction
105
137
  │ ├── cli-helpers.ts # CLI utilities
106
- ├── github.ts # GitHub URL parsing
107
- │ └── github.test.ts # Tests for GitHub utils
108
- ├── Brewfile # Package definitions (at repo root)
138
+ └── github.ts # GitHub URL parsing
139
+ ├── Brewfile # Package definitions (at repo root, embedded in binary)
109
140
  └── package.json # Project configuration
110
141
  ```
111
142
 
112
- ## Future Commands
113
-
114
- - `dotfiles` - Manage dotfiles and configurations
115
- - `macos` - Configure macOS system preferences
116
- - `setup` - Full system setup wizard
117
-
118
- ## Contributing
119
-
120
- 1. Fork the repository
121
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
122
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
123
- 4. Push to the branch (`git push origin feature/amazing-feature`)
124
- 5. Open a Pull Request
125
-
126
143
  ## License
127
144
 
128
145
  MIT
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.cs.allow-jit</key>
6
+ <true/>
7
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
8
+ <true/>
9
+ <key>com.apple.security.cs.disable-executable-page-protection</key>
10
+ <true/>
11
+ <key>com.apple.security.cs.allow-dyld-environment-variables</key>
12
+ <true/>
13
+ <key>com.apple.security.cs.disable-library-validation</key>
14
+ <true/>
15
+ </dict>
16
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pondorasti",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for pondorasti",
5
5
  "author": "Alexandru Ţurcanu",
6
6
  "license": "MIT",
@@ -20,9 +20,11 @@
20
20
  "pd": "./src/index.ts"
21
21
  },
22
22
  "scripts": {
23
- "start": "bun run src/index.ts",
24
- "dev": "bun --watch src/index.ts",
25
- "test": "bun test"
23
+ "test": "bun test",
24
+ "prepublishOnly": "bun test",
25
+ "build": "bun run build:compile && bun run build:sign",
26
+ "build:compile": "bun build --compile --minify --outfile=dist/pd src/index.ts",
27
+ "build:sign": "codesign --deep --force --sign - --entitlements entitlements.plist dist/pd"
26
28
  },
27
29
  "dependencies": {
28
30
  "ink": "^6.5.1",
@@ -0,0 +1,69 @@
1
+ import type { CommandModule } from "yargs"
2
+ import { Dock } from "../tools/dock"
3
+ import { Homebrew } from "../tools/homebrew"
4
+ import { OhMyZsh } from "../tools/ohmyzsh"
5
+
6
+ // -------------------------------------------------------------------------------------------------------------------
7
+ // Bootstrap Command - Bootstraps a fresh machine
8
+ // -------------------------------------------------------------------------------------------------------------------
9
+
10
+ const bootstrapCommand: CommandModule = {
11
+ command: "bootstrap",
12
+ describe: "Bootstrap a fresh machine with all tools and packages",
13
+ handler: async () => {
14
+ console.log("🚀 Bootstrapping fresh machine...\n")
15
+
16
+ // Step 1: Clear Dock
17
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
18
+ console.log("Step 1/4: Clear Dock")
19
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
20
+ Dock.clear()
21
+ console.log("✓ Dock cleared")
22
+
23
+ // Step 2: Install Oh My Zsh
24
+ console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
25
+ console.log("Step 2/4: Oh My Zsh")
26
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
27
+ try {
28
+ await OhMyZsh.install()
29
+ } catch (error) {
30
+ console.error("✗ Failed to install Oh My Zsh")
31
+ process.exit(1)
32
+ }
33
+
34
+ // Step 3: Install Homebrew
35
+ console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
36
+ console.log("Step 3/4: Homebrew")
37
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
38
+ try {
39
+ await Homebrew.install()
40
+ } catch (error) {
41
+ console.error("✗ Failed to install Homebrew")
42
+ process.exit(1)
43
+ }
44
+
45
+ // Step 4: Install packages from Brewfile
46
+ console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
47
+ console.log("Step 4/4: Install Packages (Brewfile)")
48
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
49
+ try {
50
+ await Homebrew.bundle()
51
+ } catch (error) {
52
+ console.error("✗ Failed to run brew bundle")
53
+ process.exit(1)
54
+ }
55
+
56
+ // Done!
57
+ console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
58
+ console.log("✅ Bootstrap complete!")
59
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
60
+ console.log("\nNext steps:")
61
+ console.log(" 1. Restart your terminal to load shell changes")
62
+ console.log(" 2. Run 'pd clone pondorasti/pondorasti' to get the repo")
63
+ console.log(" 3. Run 'pd dotfiles link' to set up dotfiles")
64
+ },
65
+ }
66
+
67
+ // -------------------------------------------------------------------------------------------------------------------
68
+
69
+ export { bootstrapCommand }
@@ -1,9 +1,6 @@
1
1
  import type { CommandModule } from "yargs"
2
- import { $ } from "bun"
3
2
  import { Homebrew } from "../tools/homebrew"
4
3
  import { failHandler } from "../utils/cli-helpers"
5
- import * as path from "path"
6
- import * as fs from "fs"
7
4
 
8
5
  // -------------------------------------------------------------------------------------------------------------------
9
6
  // Subcommands
@@ -13,21 +10,8 @@ const installCommand: CommandModule = {
13
10
  command: "install",
14
11
  describe: "Install Homebrew",
15
12
  handler: async () => {
16
- if (Homebrew.isInstalled()) {
17
- const brewPath = Homebrew.getBrewPath()
18
- console.log(`✓ Homebrew is already installed at ${brewPath}`)
19
-
20
- try {
21
- await $`brew --version`
22
- } catch (error) {
23
- console.error("⚠ Homebrew is installed but not working properly")
24
- }
25
-
26
- return
27
- }
28
-
29
13
  try {
30
- await $`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
14
+ await Homebrew.install()
31
15
  } catch (error) {
32
16
  console.error("✗ Failed to install Homebrew")
33
17
  process.exit(1)
@@ -39,24 +23,14 @@ const bundleCommand: CommandModule = {
39
23
  command: "bundle",
40
24
  describe: "Run brew bundle from Brewfile",
41
25
  handler: async () => {
42
- if (!Homebrew.isInstalled()) {
43
- console.error("✗ Homebrew is not installed. Run 'pondorasti brew install' first.")
44
- process.exit(1)
45
- }
46
-
47
- // Brewfile is always at the root of the pondorasti repo
48
- const brewfilePath = path.join(__dirname, "..", "..", "..", "..", "Brewfile")
49
-
50
- if (!fs.existsSync(brewfilePath)) {
51
- console.error("✗ Brewfile not found at expected location:", brewfilePath)
52
- console.error("This is likely a bug in the CLI configuration.")
53
- process.exit(1)
54
- }
55
-
56
26
  try {
57
- await Homebrew.runBundle(brewfilePath)
27
+ await Homebrew.bundle()
58
28
  } catch (error) {
59
- console.error("✗ Failed to run brew bundle")
29
+ if (error instanceof Error) {
30
+ console.error(`✗ ${error.message}`)
31
+ } else {
32
+ console.error("✗ Failed to run brew bundle")
33
+ }
60
34
  process.exit(1)
61
35
  }
62
36
  },
@@ -70,20 +44,11 @@ const brewCommand: CommandModule = {
70
44
  command: "brew",
71
45
  describe: "Manage Homebrew and install packages",
72
46
  builder: (yargs) => {
73
- return yargs //
74
- .command(installCommand)
75
- .command(bundleCommand)
76
- .demandCommand(1)
77
- .help()
78
- .strict()
79
- .fail(failHandler)
80
- },
81
- handler: () => {
82
- // This handler is called when no subcommand is provided
83
- // But the fail handler above will handle it
47
+ return yargs.command(installCommand).command(bundleCommand).demandCommand(1).help().strict().fail(failHandler)
84
48
  },
49
+ handler: () => {},
85
50
  }
86
51
 
87
52
  // -------------------------------------------------------------------------------------------------------------------
88
53
 
89
- export default brewCommand
54
+ export { brewCommand }
@@ -80,4 +80,6 @@ const cloneCommand: CommandModule<{}, { url: string }> = {
80
80
  },
81
81
  }
82
82
 
83
- export default cloneCommand
83
+ // -------------------------------------------------------------------------------------------------------------------
84
+
85
+ export { cloneCommand }
@@ -0,0 +1,129 @@
1
+ import type { CommandModule } from "yargs"
2
+ import { Defaults } from "../tools/defaults"
3
+ import { failHandler } from "../utils/cli-helpers"
4
+
5
+ // -------------------------------------------------------------------------------------------------------------------
6
+ // Helpers
7
+ // -------------------------------------------------------------------------------------------------------------------
8
+
9
+ function statusIcon(matches: boolean): string {
10
+ return matches ? "✓" : "○"
11
+ }
12
+
13
+ function statusColor(matches: boolean): string {
14
+ return matches ? "\x1b[32m" : "\x1b[33m" // green or yellow
15
+ }
16
+
17
+ const reset = "\x1b[0m"
18
+ const dim = "\x1b[90m"
19
+
20
+ // -------------------------------------------------------------------------------------------------------------------
21
+ // Subcommands
22
+ // -------------------------------------------------------------------------------------------------------------------
23
+
24
+ const applyCommand: CommandModule = {
25
+ command: "apply",
26
+ describe: "Apply all macOS defaults",
27
+ handler: async () => {
28
+ console.log("\nApplying macOS defaults...\n")
29
+
30
+ const result = Defaults.apply()
31
+
32
+ for (const def of result.applied) {
33
+ console.log(` \x1b[32m✓${reset} ${def.description}`)
34
+ }
35
+
36
+ for (const def of result.skipped) {
37
+ console.log(` ${dim}✓ ${def.description} (already set)${reset}`)
38
+ }
39
+
40
+ for (const { def, error } of result.errors) {
41
+ console.log(` \x1b[31m✗${reset} ${def.description}`)
42
+ console.log(` ${dim}${error}${reset}`)
43
+ }
44
+
45
+ console.log()
46
+
47
+ if (result.applied.length > 0) {
48
+ console.log(`Applied ${result.applied.length} default(s)`)
49
+ }
50
+ if (result.skipped.length > 0) {
51
+ console.log(`${dim}Skipped ${result.skipped.length} (already set)${reset}`)
52
+ }
53
+ if (result.errors.length > 0) {
54
+ console.log(`\x1b[31mFailed: ${result.errors.length}${reset}`)
55
+ }
56
+
57
+ console.log()
58
+ },
59
+ }
60
+
61
+ const listCommand: CommandModule = {
62
+ command: "list",
63
+ describe: "List all macOS defaults that will be applied",
64
+ handler: async () => {
65
+ console.log("\nmacOS defaults:\n")
66
+
67
+ const defaults = Defaults.config
68
+
69
+ for (const def of defaults) {
70
+ console.log(` ${def.description}`)
71
+ console.log(` ${dim}${def.domain} ${def.key} = ${Defaults.formatValue(def.value)}${reset}`)
72
+ }
73
+
74
+ console.log()
75
+ console.log(`Total: ${defaults.length} default(s)`)
76
+ console.log()
77
+ },
78
+ }
79
+
80
+ const statusCommand: CommandModule = {
81
+ command: "status",
82
+ describe: "Show current status of macOS defaults",
83
+ handler: async () => {
84
+ const statuses = Defaults.getStatus()
85
+
86
+ for (const { def, current, matches } of statuses) {
87
+ console.log(` ${statusColor(matches)}${statusIcon(matches)}${reset} ${def.description}`)
88
+ if (!matches) {
89
+ console.log(` ${dim}Current: ${Defaults.formatValue(current)}${reset}`)
90
+ console.log(` ${dim}Target: ${Defaults.formatValue(def.value)}${reset}`)
91
+ }
92
+ }
93
+
94
+ const matched = statuses.filter((s) => s.matches).length
95
+ const unmatched = statuses.length - matched
96
+
97
+ console.log()
98
+ if (unmatched > 0) {
99
+ console.log(`${unmatched} default(s) need to be applied`)
100
+ } else {
101
+ console.log(`All ${matched} default(s) are set correctly`)
102
+ }
103
+ console.log()
104
+ },
105
+ }
106
+
107
+ // -------------------------------------------------------------------------------------------------------------------
108
+ // Main defaults command
109
+ // -------------------------------------------------------------------------------------------------------------------
110
+
111
+ const defaultsCommand: CommandModule = {
112
+ command: "defaults",
113
+ describe: "Manage macOS system defaults",
114
+ builder: (yargs) => {
115
+ return yargs
116
+ .command(applyCommand)
117
+ .command(listCommand)
118
+ .command(statusCommand)
119
+ .demandCommand(1)
120
+ .help()
121
+ .strict()
122
+ .fail(failHandler)
123
+ },
124
+ handler: () => {},
125
+ }
126
+
127
+ // -------------------------------------------------------------------------------------------------------------------
128
+
129
+ export { defaultsCommand }
@@ -0,0 +1,34 @@
1
+ import type { CommandModule } from "yargs"
2
+ import { Dock } from "../tools/dock"
3
+ import { failHandler } from "../utils/cli-helpers"
4
+
5
+ // -------------------------------------------------------------------------------------------------------------------
6
+ // Subcommands
7
+ // -------------------------------------------------------------------------------------------------------------------
8
+
9
+ const clearCommand: CommandModule = {
10
+ command: "clear",
11
+ describe: "Remove all pinned apps from the Dock",
12
+ handler: async () => {
13
+ console.log("Clearing Dock...")
14
+ Dock.clear()
15
+ console.log("✓ Dock cleared")
16
+ },
17
+ }
18
+
19
+ // -------------------------------------------------------------------------------------------------------------------
20
+ // Main dock command
21
+ // -------------------------------------------------------------------------------------------------------------------
22
+
23
+ const dockCommand: CommandModule = {
24
+ command: "dock",
25
+ describe: "Manage macOS Dock",
26
+ builder: (yargs) => {
27
+ return yargs.command(clearCommand).demandCommand(1).help().strict().fail(failHandler)
28
+ },
29
+ handler: () => {},
30
+ }
31
+
32
+ // -------------------------------------------------------------------------------------------------------------------
33
+
34
+ export { dockCommand }