Skip to content

Commit fcd9114

Browse files
committed
playbook: add 60-second-darwin playbook
Add a variant of 60-second-linux for macOS (darwin). This toolbox doesn't use Nix, since PRoot (or equivalent) is not available for macOS.
1 parent eb1ad39 commit fcd9114

File tree

5 files changed

+90
-24
lines changed

5 files changed

+90
-24
lines changed

.github/workflows/build-toolbox.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ jobs:
2222
- runner: ubuntu-24.04-arm
2323
os: linux
2424
arch: arm64
25-
# - runner: macos-13
26-
# os: darwin
27-
# arch: amd64
28-
# - runner: macos-15
29-
# os: darwin
30-
# arch: arm64
25+
- runner: macos-13
26+
os: darwin
27+
arch: amd64
28+
- runner: macos-15
29+
os: darwin
30+
arch: arm64
3131
steps:
3232
- name: Checkout
3333
uses: actions/checkout@v5
@@ -49,13 +49,22 @@ jobs:
4949
cd toolbox
5050
go build -o toolbox-builder
5151
52-
- name: Build toolbox archive
52+
- name: Build toolbox archive (Linux)
53+
if: matrix.os == 'linux'
5354
shell: bash
5455
run: |
5556
set -euo pipefail
5657
cd toolbox
5758
./toolbox-builder --playbook ../playbook/60-second-linux.yaml --out .
5859
60+
- name: Build toolbox archive (Darwin)
61+
if: matrix.os == 'darwin'
62+
shell: bash
63+
run: |
64+
set -euo pipefail
65+
cd toolbox
66+
./toolbox-builder --playbook ../playbook/60-second-darwin.yaml --out .
67+
5968
- name: Upload artifact
6069
uses: actions/upload-artifact@v4
6170
with:

app/toolbox.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import (
1414
"strings"
1515
"time"
1616

17+
"gradient-engineer/playbook"
18+
1719
"github.com/ulikunitz/xz"
1820
"gopkg.in/yaml.v3"
19-
"gradient-engineer/playbook"
2021
)
2122

2223
// DiagnosticCommand represents a diagnostic command with its actual command and display name
@@ -217,7 +218,11 @@ func (t *Toolbox) GetDiagnosticCommands() ([]DiagnosticCommand, error) {
217218
cmdStr = prootPrefix + " " + resolved
218219
}
219220
} else {
220-
return nil, fmt.Errorf("binary for command '%s' not found in toolbox nix store", binName)
221+
if runtime.GOOS == "darwin" {
222+
cmdStr = c.Command // Nix + PRoot not available on macOS
223+
} else {
224+
return nil, fmt.Errorf("binary for command '%s' not found in toolbox nix store", binName)
225+
}
221226
}
222227

223228
timeout := 5 * time.Second

app/ui.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,13 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
154154
}
155155
m.downloaded = true
156156
// Populate commands now that toolbox is available
157-
m.commands, _ = m.toolbox.GetDiagnosticCommands()
157+
commands, err := m.toolbox.GetDiagnosticCommands()
158+
if err != nil {
159+
fmt.Println(err)
160+
m.done = true
161+
return m, tea.Quit
162+
}
163+
m.commands = commands
158164
n := len(m.commands)
159165
m.statuses = make([]commandStatus, n)
160166
m.outputs = make([]string, n)

playbook/60-second-darwin.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
id: 60-second-darwin
2+
system_prompt: |
3+
### 60-second macOS analysis
4+
5+
Please analyze the following macOS system diagnostic output. Focus on identifying any performance issues, errors, or notable system characteristics.
6+
7+
- Be concise but comprehensive
8+
- Start with a 1-sentence overview and the key finding
9+
- Prefer bullet points where helpful
10+
- Keep the total length to 2-3 short paragraphs (including bullets)
11+
12+
When recommending actions, prioritize practical steps.
13+
commands:
14+
- command: uptime
15+
description: System uptime and load averages
16+
- command: log show --style syslog --last 1m
17+
description: Recent system log entries (last 1 minute)
18+
- command: vm_stat 1
19+
description: Virtual memory paging, 1s updates
20+
- command: ps -M -o pid,%cpu,comm -r
21+
description: Threads sorted by CPU to spot hot single-thread bottlenecks
22+
- command: top -l 999 -s 1 -o cpu
23+
description: Per-process CPU usage, 1s rolling view
24+
- command: iostat -d -w 1
25+
description: Disk I/O per device, 1s updates (KB/t, tps, MB/s)
26+
- command: memory_pressure -Q
27+
description: Memory pressure summary (free, purgeable, compressed)
28+
- command: netstat -w 1 -i
29+
description: Network device statistics for all interfaces, 1s updates
30+
- command: netstat -s -p tcp
31+
description: TCP health snapshot (retransmits, connection stats)
32+
- command: top -l 1
33+
description: One-shot system snapshot (CPU breakdown, processes, PhysMem)
34+
35+

toolbox/generate.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import (
1111
"runtime"
1212
"time"
1313

14+
"gradient-engineer/playbook"
15+
1416
"github.com/spf13/cobra"
1517
"gopkg.in/yaml.v3"
16-
"gradient-engineer/playbook"
1718
)
1819

1920
var (
@@ -33,9 +34,6 @@ target systems.`,
3334
if playbookPath == "" {
3435
return fmt.Errorf("playbook path is required")
3536
}
36-
if runtime.GOOS != "linux" {
37-
return fmt.Errorf("this utility must run on Linux")
38-
}
3937
return nil
4038
},
4139
RunE: func(cmd *cobra.Command, args []string) error {
@@ -46,7 +44,7 @@ target systems.`,
4644
// Define flags
4745
rootCmd.Flags().StringVarP(&playbookPath, "playbook", "p", "", "Path to playbook file (required)")
4846
rootCmd.Flags().StringVarP(&outDir, "out", "o", ".", "Output directory for generated archive")
49-
47+
5048
// Mark required flags
5149
rootCmd.MarkFlagRequired("playbook")
5250

@@ -61,8 +59,10 @@ func generateToolbox() error {
6159
if err != nil {
6260
return fmt.Errorf("failed to read playbook: %w", err)
6361
}
64-
if len(cfg.Nixpkgs.Packages) == 0 {
65-
return fmt.Errorf("no nixpkgs.packages listed in %s", playbookPath)
62+
if runtime.GOOS == "linux" {
63+
if len(cfg.Nixpkgs.Packages) == 0 {
64+
return fmt.Errorf("no nixpkgs.packages listed in %s", playbookPath)
65+
}
6666
}
6767

6868
workDir, err := os.MkdirTemp("", "toolbox_work_*")
@@ -75,12 +75,15 @@ func generateToolbox() error {
7575
}()
7676

7777
toolboxDir, _ := filepath.Abs(filepath.Join(workDir, "toolbox"))
78-
if err := nixCopy(toolboxDir, cfg.Nixpkgs.Version, cfg.Nixpkgs.Packages); err != nil {
79-
return fmt.Errorf("nix copy failed: %w", err)
80-
}
8178

82-
if err := fetchAndInstallProot(toolboxDir); err != nil {
83-
return fmt.Errorf("failed to install proot: %w", err)
79+
if runtime.GOOS == "linux" {
80+
if err := nixCopy(toolboxDir, cfg.Nixpkgs.Version, cfg.Nixpkgs.Packages); err != nil {
81+
return fmt.Errorf("nix copy failed: %w", err)
82+
}
83+
84+
if err := fetchAndInstallProot(toolboxDir); err != nil {
85+
return fmt.Errorf("failed to install proot: %w", err)
86+
}
8487
}
8588

8689
// Include the playbook file inside the toolbox directory
@@ -261,9 +264,17 @@ func createTarXz(outPath string, dir string) error {
261264
parent := filepath.Dir(dir)
262265
base := filepath.Base(dir)
263266

264-
cmd := exec.Command("tar", "-I", "xz -e -9 -T0", "-cf", outPath, base)
267+
if runtime.GOOS == "linux" {
268+
cmd := exec.Command("tar", "-I", "xz -e -9 -T0", "-cf", outPath, base)
269+
cmd.Dir = parent
270+
cmd.Stdout = os.Stdout
271+
cmd.Stderr = os.Stderr
272+
return cmd.Run()
273+
}
274+
275+
cmd := exec.Command("tar", "-cJf", outPath, "--options", "xz:compression-level=9", base)
265276
cmd.Dir = parent
266277
cmd.Stdout = os.Stdout
267278
cmd.Stderr = os.Stderr
268279
return cmd.Run()
269-
}
280+
}

0 commit comments

Comments
 (0)