Skip to content

Commit ed4258b

Browse files
committed
use Homebrew on both platforms, remove sudo entirely
System packages and cloud CLIs (aws, azure, gcloud) now install via Homebrew on both Linux and macOS — the apt codepath, custom cloud-CLI installers, and all sudo handling are gone. gcloud-cli uses the cask (has proper Linux support as of brew 4.5+). cps binary now lives at ~/.local/bin/cps; self-update uses os.Executable + atomic rename. Also fix EXDEV on Linux tmpfs /tmp by keeping neovim/go extract temp dirs inside ~/shell.
1 parent ee226fa commit ed4258b

13 files changed

Lines changed: 58 additions & 354 deletions

File tree

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ A single Go binary (`cps`) that sets up and manages a complete CLI development e
1515
|---|---|
1616
| [Oh My Zsh](https://ohmyz.sh/) | Shell framework — `cps init` will not run without it |
1717
| Git | Used to clone plugins and configs |
18-
| [Homebrew](https://brew.sh/) (macOS only) | System package installs on macOS |
18+
| [Homebrew](https://brew.sh/) | System package installs (both Linux and macOS) — `cps` installs all system/cloud CLI packages via brew |
1919

2020
**Recommended:**
2121

@@ -26,10 +26,13 @@ A single Go binary (`cps`) that sets up and manages a complete CLI development e
2626

2727
```bash
2828
ARCH=$(uname -m); [ "$ARCH" = "x86_64" ] && ARCH=amd64; [ "$ARCH" = "aarch64" ] && ARCH=arm64
29-
curl -sL "https://github.com/tanq16/cli-productivity-suite/releases/latest/download/cps-$(uname -s | tr '[:upper:]' '[:lower:]')-$ARCH" -o cps
30-
chmod +x cps && sudo mv cps /usr/local/bin/
29+
mkdir -p "$HOME/.local/bin"
30+
curl -sL "https://github.com/tanq16/cli-productivity-suite/releases/latest/download/cps-$(uname -s | tr '[:upper:]' '[:lower:]')-$ARCH" -o "$HOME/.local/bin/cps"
31+
chmod +x "$HOME/.local/bin/cps"
3132
```
3233

34+
If `~/.local/bin` isn't on your PATH yet (common on fresh macOS), run `cps init` via its full path — `~/.local/bin/cps init` — for the first invocation. The rc fragment that `init` deploys adds `~/.local/bin` to PATH for all future sessions.
35+
3336
Or build from source:
3437

3538
```bash
@@ -41,7 +44,7 @@ make build # produces ./cps
4144

4245
### `cps init`
4346

44-
Sets up the base environment — core CLI tools (bat, fd, ripgrep, lsd, jq, yq, fzf, sd, gron, sq, zoxide, gh, anbu, danzo, ai-context), Neovim with NvChad, zsh plugins, tmux with TPM, and config files. Requires sudo on Linux for system packages.
47+
Sets up the base environment — core CLI tools (bat, fd, ripgrep, lsd, jq, yq, fzf, sd, gron, sq, zoxide, gh, anbu, danzo, ai-context), Neovim with NvChad, zsh plugins, tmux with TPM, and config files. System packages install via Homebrew on both Linux and macOS — no sudo required.
4548

4649
```bash
4750
cps init
@@ -80,7 +83,7 @@ Terminal cheat sheets — `cps`, `go`, `uv`, `fnm`, `rust`, `tmux`, `nvim`, `fzf
8083

8184
### `cps self-update`
8285

83-
Updates the `cps` binary to the latest release. Requires sudo.
86+
Updates the `cps` binary in place (at whatever path it's running from).
8487

8588
### Flags
8689

@@ -148,6 +151,8 @@ rm -rf $HOME/shell $HOME/.tmux $HOME/.config/nvim $HOME/.config/cps
148151
# Configs
149152
rm -f $HOME/.tmux.conf $HOME/.zshrc $HOME/.aerospace.toml $HOME/.config/kitty/kitty.conf $HOME/.config/kitty/current-theme.conf
150153
rm -rf $HOME/.local/share/nvim $HOME/.oh-my-zsh
151-
sudo rm -f /usr/local/bin/cps
152-
sudo rm -rf /usr/local/aws-cli
154+
rm -f $HOME/.local/bin/cps
155+
156+
# Uninstall brew-managed system and cloud packages (optional)
157+
brew uninstall awscli azure-cli gcloud-cli
153158
```

internal/configs/rc-base.zsh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1+
# --- Homebrew ---
2+
export HOMEBREW_NO_AUTO_UPDATE=1
3+
if [ -x /opt/homebrew/bin/brew ]; then
4+
eval "$(/opt/homebrew/bin/brew shellenv)"
5+
elif [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then
6+
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
7+
elif [ -x /usr/local/bin/brew ]; then
8+
eval "$(/usr/local/bin/brew shellenv)"
9+
fi
10+
111
# --- Oh My Zsh ---
212
export ZSH="$HOME/.oh-my-zsh"
313
ZSH_THEME="spaceship"
414
plugins=(zsh-autosuggestions zsh-syntax-highlighting)
515
source $ZSH/oh-my-zsh.sh
616

717
# --- PATH ---
8-
export PATH="$HOME/shell/extensions:$HOME/shell/executables:/opt/homebrew/bin:$HOME/.local/bin:$PATH"
18+
export PATH="$HOME/shell/extensions:$HOME/shell/executables:$HOME/.local/bin:$PATH"
919

1020
# --- FZF ---
1121
[ -f "$HOME/shell/completions/fzf.zsh" ] && source "$HOME/shell/completions/fzf.zsh"

internal/configs/rc-cloud.zsh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# --- gcloud ---
2-
[ -f "$HOME/shell/gcloud-sdk/path.zsh.inc" ] && source "$HOME/shell/gcloud-sdk/path.zsh.inc"
3-
[ -f "$HOME/shell/gcloud-sdk/completion.zsh.inc" ] && source "$HOME/shell/gcloud-sdk/completion.zsh.inc"
2+
if [ -n "$HOMEBREW_PREFIX" ]; then
3+
[ -f "$HOMEBREW_PREFIX/share/google-cloud-sdk/path.zsh.inc" ] && source "$HOMEBREW_PREFIX/share/google-cloud-sdk/path.zsh.inc"
4+
[ -f "$HOMEBREW_PREFIX/share/google-cloud-sdk/completion.zsh.inc" ] && source "$HOMEBREW_PREFIX/share/google-cloud-sdk/completion.zsh.inc"
5+
fi
46

57
# --- AWS CLI ---
68
alias awsn='aws --no-cli-pager'

internal/installer/cloud_cli.go

Lines changed: 0 additions & 134 deletions
This file was deleted.

internal/installer/installer.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ func Dispatch(kind registry.ToolKind) Installer {
2727
return &DirectDownloadInstaller{}
2828
case registry.SystemPackage:
2929
return &SystemPackageInstaller{}
30-
case registry.CloudCLI:
31-
return &CloudCLIInstaller{}
3230
case registry.LanguageRuntime:
3331
return &LanguageRuntimeInstaller{}
3432
case registry.ConfigFile:

internal/installer/language_runtime.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ func (l *LanguageRuntimeInstaller) installNeovim(p platform.Platform, gh *github
6060

6161
url := fmt.Sprintf("https://github.com/neovim/neovim/releases/download/stable/nvim-%s-%s.tar.gz", osStr, archStr)
6262

63-
tmpDir, err := os.MkdirTemp("", "cps-neovim-*")
63+
// Temp dir inside p.ShellDir() so os.Rename stays on the same filesystem (avoids EXDEV on Linux tmpfs /tmp).
64+
tmpDir, err := os.MkdirTemp(p.ShellDir(), "cps-neovim-*")
6465
if err != nil {
6566
return Result{Tool: "neovim", Err: err}
6667
}
@@ -154,7 +155,8 @@ func (l *LanguageRuntimeInstaller) installGo(p platform.Platform, st *state.Stat
154155
return Result{Tool: "go-sdk", Err: fmt.Errorf("no Go download found for %s/%s", p.OS, p.Arch)}
155156
}
156157

157-
tmpDir, err := os.MkdirTemp("", "cps-go-*")
158+
// Temp dir inside p.ShellDir() so os.Rename stays on the same filesystem (avoids EXDEV on Linux tmpfs /tmp).
159+
tmpDir, err := os.MkdirTemp(p.ShellDir(), "cps-go-*")
158160
if err != nil {
159161
return Result{Tool: "go-sdk", Err: err}
160162
}

internal/installer/system_package.go

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,7 @@ import (
1313

1414
type SystemPackageInstaller struct{}
1515

16-
func (s *SystemPackageInstaller) Install(tool *registry.Tool, p platform.Platform, _ *github.Client, st *state.State) Result {
17-
switch p.OS {
18-
case platform.Linux:
19-
return s.installApt(tool, st)
20-
case platform.Darwin:
21-
return s.installBrew(tool, st)
22-
default:
23-
return Result{Tool: tool.Name, Err: fmt.Errorf("unsupported OS")}
24-
}
25-
}
26-
27-
func (s *SystemPackageInstaller) installApt(tool *registry.Tool, st *state.State) Result {
28-
if len(tool.AptPkgs) == 0 {
29-
return Result{Tool: tool.Name, Skipped: true}
30-
}
31-
32-
args := append([]string{"apt-get", "install", "-y"}, tool.AptPkgs...)
33-
cmd := exec.Command("sudo", args...)
34-
if err := utils.RunCmd(cmd); err != nil {
35-
return Result{Tool: tool.Name, Err: fmt.Errorf("apt install failed: %w", err)}
36-
}
37-
38-
st.SetToolVersion(tool.Name, "system-managed")
39-
return Result{Tool: tool.Name, Version: "system-managed"}
40-
}
41-
42-
func (s *SystemPackageInstaller) installBrew(tool *registry.Tool, st *state.State) Result {
16+
func (s *SystemPackageInstaller) Install(tool *registry.Tool, _ platform.Platform, _ *github.Client, st *state.State) Result {
4317
if len(tool.BrewPkgs) == 0 && len(tool.BrewCasks) == 0 {
4418
return Result{Tool: tool.Name, Skipped: true}
4519
}
@@ -61,6 +35,6 @@ func (s *SystemPackageInstaller) installBrew(tool *registry.Tool, st *state.Stat
6135
}
6236
}
6337

64-
st.SetToolVersion(tool.Name, "system-managed")
65-
return Result{Tool: tool.Name, Version: "system-managed"}
38+
st.SetToolVersion(tool.Name, "brew-managed")
39+
return Result{Tool: tool.Name, Version: "brew-managed"}
6640
}

internal/registry/manifest.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,12 @@ var AllTools = []Tool{
163163
{
164164
Name: "core-utils", Kind: SystemPackage, Category: System,
165165
Description: "Core system utilities",
166-
AptPkgs: []string{"git", "wget", "curl", "zip", "unzip", "file"},
167-
BrewPkgs: []string{"git", "wget", "curl"},
166+
BrewPkgs: []string{"git", "wget", "curl", "zip", "unzip", "file"},
168167
},
169168
{
170169
Name: "shell-base", Kind: SystemPackage, Category: System,
171170
Description: "Shell and terminal essentials",
172-
AptPkgs: []string{"tmux", "zsh", "htop"},
173-
BrewPkgs: []string{"tmux", "htop"},
171+
BrewPkgs: []string{"tmux", "zsh", "htop"},
174172
},
175173

176174
// ========== Language Runtimes (base only) ==========

internal/registry/manifest_extensions.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -450,18 +450,16 @@ var extensionPacks = []ExtensionPack{
450450
Name: "dev-tools", Kind: SystemPackage, Category: ExtSystem, Extension: true,
451451
Description: "Development build tools",
452452
Platforms: []string{"linux"},
453-
AptPkgs: []string{"cmake", "gcc", "make", "ninja-build", "gettext"},
453+
BrewPkgs: []string{"cmake", "gcc", "make", "ninja", "gettext"},
454454
},
455455
{
456456
Name: "network-tools", Kind: SystemPackage, Category: ExtSystem, Extension: true,
457457
Description: "Network utilities",
458-
AptPkgs: []string{"nmap", "ncat", "openssl"},
459-
BrewPkgs: []string{"openssl", "nmap"},
458+
BrewPkgs: []string{"nmap", "openssl"},
460459
},
461460
{
462461
Name: "media-tools", Kind: SystemPackage, Category: ExtSystem, Extension: true,
463462
Description: "Media and monitoring tools",
464-
AptPkgs: []string{"ffmpeg"},
465463
BrewPkgs: []string{"ffmpeg"},
466464
},
467465
{
@@ -478,16 +476,19 @@ var extensionPacks = []ExtensionPack{
478476
Category: ExtCloud,
479477
Tools: []Tool{
480478
{
481-
Name: "aws-cli", Kind: CloudCLI, Category: ExtCloud, Extension: true,
479+
Name: "aws-cli", Kind: SystemPackage, Category: ExtCloud, Extension: true,
482480
Description: "AWS CLI v2",
481+
BrewPkgs: []string{"awscli"},
483482
},
484483
{
485-
Name: "azure-cli", Kind: CloudCLI, Category: ExtCloud, Extension: true,
484+
Name: "azure-cli", Kind: SystemPackage, Category: ExtCloud, Extension: true,
486485
Description: "Azure CLI",
486+
BrewPkgs: []string{"azure-cli"},
487487
},
488488
{
489-
Name: "gcloud-cli", Kind: CloudCLI, Category: ExtCloud, Extension: true,
489+
Name: "gcloud-cli", Kind: SystemPackage, Category: ExtCloud, Extension: true,
490490
Description: "Google Cloud CLI",
491+
BrewCasks: []string{"gcloud-cli"},
491492
},
492493
},
493494
},

0 commit comments

Comments
 (0)