Skip to content

Commit 19cc470

Browse files
committed
fix(macos): resolve service installation issues and add Go binary signing
**Problem 1: Inconsistent developer names in Background Activity** - Electron app signed as "Yaroslav Podieiapolskii" - Go binaries (px, px-service) were unsigned - Caused macOS to show different developer names in system dialogs **Solution 1: Add Go binary code signing** - Added "Sign Go Binaries" step in macOS workflow after Go builds - Uses same CODESIGN_IDENTITY from GitHub secrets - Signs both px and px-service with runtime hardening + timestamp - Applies same entitlements as Electron app - Now all binaries show consistent developer identity **Problem 2: Conflicting success/error messages during installation** - Electron invoked osascript with admin privileges - Go code internally invoked osascript again (double elevation) - Caused timeout/conflict but service still installed - UI showed contradictory notifications **Solution 2: Smart privilege detection in installer_darwin.go** - Check if already running as root (os.Geteuid() == 0) - If root: execute commands directly (cp, chmod, launchctl) - If not root: use osascript for privilege elevation - Applies to install/uninstall/start/stop functions - Eliminates double osascript calls when launched via Electron **Technical changes:** - .github/workflows/release.yml: Added codesign step after Go builds - src-service/installer_darwin.go: Added isRoot checks in all service functions This resolves both the developer identity inconsistency and the installation notification issues on macOS. https://claude.ai/code/session_01U9NtT9hmX68VAbYp5x2Pi1
1 parent 451efdb commit 19cc470

File tree

2 files changed

+92
-20
lines changed

2 files changed

+92
-20
lines changed

.github/workflows/release.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,32 @@ jobs:
346346
go mod download
347347
go build -ldflags "-s -w" -o px-service .
348348
349+
- name: Sign Go Binaries
350+
run: |
351+
echo "Signing Go binaries with identity: $CODESIGN_IDENTITY"
352+
353+
# Sign px binary
354+
codesign --force \
355+
--sign "$CODESIGN_IDENTITY" \
356+
--options runtime \
357+
--timestamp \
358+
--entitlements build/entitlements.mac.plist \
359+
src-go/px
360+
361+
# Sign px-service binary
362+
codesign --force \
363+
--sign "$CODESIGN_IDENTITY" \
364+
--options runtime \
365+
--timestamp \
366+
--entitlements build/entitlements.mac.plist \
367+
src-service/px-service
368+
369+
# Verify signatures
370+
codesign --verify --verbose src-go/px
371+
codesign --verify --verbose src-service/px-service
372+
373+
echo "Go binaries signed successfully"
374+
349375
- name: Sync VERSION to package.json
350376
run: node build/sync-version.js
351377

src-service/installer_darwin.go

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,29 @@ func installServiceDarwin() error {
100100

101101
fmt.Printf("Created temporary plist file: %s\n", tmpPath)
102102

103-
// Копируем файл с привилегиями администратора
104-
installScript := fmt.Sprintf("cp '%s' '%s' && chmod 644 '%s' && launchctl load '%s'",
105-
tmpPath, plistPath, plistPath, plistPath)
103+
// Проверяем запущены ли мы уже с правами root (через Electron osascript)
104+
isRoot := os.Geteuid() == 0
106105

107-
fmt.Println("Requesting administrator privileges...")
108-
if err := executeWithPrivilege(installScript); err != nil {
109-
return fmt.Errorf("failed to install service: %w", err)
106+
if isRoot {
107+
// Уже root - выполняем команды напрямую
108+
if err := exec.Command("cp", tmpPath, plistPath).Run(); err != nil {
109+
return fmt.Errorf("failed to copy plist: %w", err)
110+
}
111+
if err := exec.Command("chmod", "644", plistPath).Run(); err != nil {
112+
return fmt.Errorf("failed to chmod plist: %w", err)
113+
}
114+
if err := exec.Command("launchctl", "load", plistPath).Run(); err != nil {
115+
return fmt.Errorf("failed to load service: %w", err)
116+
}
117+
} else {
118+
// Не root - используем osascript для запроса прав
119+
installScript := fmt.Sprintf("cp '%s' '%s' && chmod 644 '%s' && launchctl load '%s'",
120+
tmpPath, plistPath, plistPath, plistPath)
121+
122+
fmt.Println("Requesting administrator privileges...")
123+
if err := executeWithPrivilege(installScript); err != nil {
124+
return fmt.Errorf("failed to install service: %w", err)
125+
}
110126
}
111127

112128
fmt.Printf("Installed and loaded service: %s\n", plistPath)
@@ -116,14 +132,26 @@ func installServiceDarwin() error {
116132

117133
// uninstallServiceDarwin удаляет сервис на macOS
118134
func uninstallServiceDarwin() error {
119-
fmt.Println("Requesting administrator privileges...")
135+
isRoot := os.Geteuid() == 0
120136

121-
// Останавливаем и выгружаем сервис, затем удаляем plist файл
122-
uninstallScript := fmt.Sprintf("launchctl unload '%s' 2>/dev/null || true && rm -f '%s'",
123-
plistPath, plistPath)
137+
if isRoot {
138+
// Уже root - выполняем команды напрямую
139+
// Останавливаем сервис (игнорируем ошибки)
140+
exec.Command("launchctl", "unload", plistPath).Run()
124141

125-
if err := executeWithPrivilege(uninstallScript); err != nil {
126-
return fmt.Errorf("failed to uninstall service: %w", err)
142+
// Удаляем plist файл
143+
if err := os.Remove(plistPath); err != nil && !os.IsNotExist(err) {
144+
return fmt.Errorf("failed to remove plist: %w", err)
145+
}
146+
} else {
147+
// Не root - используем osascript
148+
fmt.Println("Requesting administrator privileges...")
149+
uninstallScript := fmt.Sprintf("launchctl unload '%s' 2>/dev/null || true && rm -f '%s'",
150+
plistPath, plistPath)
151+
152+
if err := executeWithPrivilege(uninstallScript); err != nil {
153+
return fmt.Errorf("failed to uninstall service: %w", err)
154+
}
127155
}
128156

129157
fmt.Printf("Uninstalled service: %s\n", plistPath)
@@ -133,25 +161,43 @@ func uninstallServiceDarwin() error {
133161

134162
// startServiceDarwin запускает сервис на macOS
135163
func startServiceDarwin() error {
136-
fmt.Println("Requesting administrator privileges...")
164+
isRoot := os.Geteuid() == 0
137165

138-
startScript := fmt.Sprintf("launchctl load '%s'", plistPath)
166+
if isRoot {
167+
// Уже root - выполняем команду напрямую
168+
if err := exec.Command("launchctl", "load", plistPath).Run(); err != nil {
169+
return fmt.Errorf("failed to start service: %w", err)
170+
}
171+
} else {
172+
// Не root - используем osascript
173+
fmt.Println("Requesting administrator privileges...")
174+
startScript := fmt.Sprintf("launchctl load '%s'", plistPath)
139175

140-
if err := executeWithPrivilege(startScript); err != nil {
141-
return fmt.Errorf("failed to start service: %w", err)
176+
if err := executeWithPrivilege(startScript); err != nil {
177+
return fmt.Errorf("failed to start service: %w", err)
178+
}
142179
}
143180

144181
return nil
145182
}
146183

147184
// stopServiceDarwin останавливает сервис на macOS
148185
func stopServiceDarwin() error {
149-
fmt.Println("Requesting administrator privileges...")
186+
isRoot := os.Geteuid() == 0
150187

151-
stopScript := fmt.Sprintf("launchctl unload '%s'", plistPath)
188+
if isRoot {
189+
// Уже root - выполняем команду напрямую
190+
if err := exec.Command("launchctl", "unload", plistPath).Run(); err != nil {
191+
return fmt.Errorf("failed to stop service: %w", err)
192+
}
193+
} else {
194+
// Не root - используем osascript
195+
fmt.Println("Requesting administrator privileges...")
196+
stopScript := fmt.Sprintf("launchctl unload '%s'", plistPath)
152197

153-
if err := executeWithPrivilege(stopScript); err != nil {
154-
return fmt.Errorf("failed to stop service: %w", err)
198+
if err := executeWithPrivilege(stopScript); err != nil {
199+
return fmt.Errorf("failed to stop service: %w", err)
200+
}
155201
}
156202

157203
return nil

0 commit comments

Comments
 (0)