Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y libayatana-appindicator3-dev libgtk-3-dev

- name: Set up Go
uses: actions/setup-go@v5
with:
Expand All @@ -39,6 +44,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libayatana-appindicator3-dev libgtk-3-dev

- name: Set up Go
uses: actions/setup-go@v5
with:
Expand All @@ -47,7 +58,13 @@ jobs:
- name: Get dependencies
run: go mod download

- name: Run tests
- name: Run tests (Unix)
if: matrix.os != 'windows-latest'
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...

- name: Run tests (Windows)
if: matrix.os == 'windows-latest'
shell: cmd
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload coverage to Codecov
Expand All @@ -69,6 +86,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libayatana-appindicator3-dev libgtk-3-dev

- name: Set up Go
uses: actions/setup-go@v5
with:
Expand Down Expand Up @@ -107,6 +130,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libayatana-appindicator3-dev libgtk-3-dev

- name: Set up Go
uses: actions/setup-go@v5
with:
Expand Down
2 changes: 2 additions & 0 deletions internal/systray/systray.go
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,8 @@ func (a *App) installCLIToPathWindows(sourcePath, targetPath string) {
}

// uninstallCLI removes the CLI binary from the system path.
//
//nolint:unused // Reserved for future use in systray menu
func (a *App) uninstallCLI() bool {
targetPath := "/usr/local/bin/agentmgr"

Expand Down
10 changes: 9 additions & 1 deletion internal/systray/windows_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

package systray

import "github.com/kevinelliott/agentmgr/pkg/agent"
import (
"github.com/kevinelliott/agentmgr/pkg/agent"
"github.com/kevinelliott/agentmgr/pkg/catalog"
)

// showNativeSettingsWindow is not available on this platform.
func (a *App) showNativeSettingsWindow() {
// Fall back to platform-specific dialog
a.showSettings()
}

// showNativeManageAgentsWindow is not available on this platform.
func (a *App) showNativeManageAgentsWindow(_ []catalog.AgentDef, _ []agent.Installation) {
// No-op on non-darwin platforms; caller should check hasNativeWindowSupport()
}

// showNativeAgentDetailsWindow is not available on this platform.
func (a *App) showNativeAgentDetailsWindow(inst agent.Installation) {
// Fall back to platform-specific dialog
Expand Down
36 changes: 27 additions & 9 deletions pkg/platform/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,32 @@ func (l *linuxPlatform) GetDataDir() string {
if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
return filepath.Join(xdgData, "agentmgr")
}
home, _ := os.UserHomeDir()
home, err := os.UserHomeDir()
if err != nil {
return filepath.Join(os.TempDir(), ".local", "share", "agentmgr")
}
return filepath.Join(home, ".local", "share", "agentmgr")
}

func (l *linuxPlatform) GetConfigDir() string {
if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
return filepath.Join(xdgConfig, "agentmgr")
}
home, _ := os.UserHomeDir()
home, err := os.UserHomeDir()
if err != nil {
return filepath.Join(os.TempDir(), ".config", "agentmgr")
}
return filepath.Join(home, ".config", "agentmgr")
}

func (l *linuxPlatform) GetCacheDir() string {
if xdgCache := os.Getenv("XDG_CACHE_HOME"); xdgCache != "" {
return filepath.Join(xdgCache, "agentmgr")
}
home, _ := os.UserHomeDir()
home, err := os.UserHomeDir()
if err != nil {
return filepath.Join(os.TempDir(), ".cache", "agentmgr")
}
return filepath.Join(home, ".cache", "agentmgr")
}

Expand All @@ -76,16 +85,17 @@ func (l *linuxPlatform) EnableAutoStart(ctx context.Context) error {
}

func (l *linuxPlatform) DisableAutoStart(ctx context.Context) error {
// Try both methods
l.disableSystemdAutoStart(ctx)
l.disableXDGAutoStart()
// Try both methods - errors are intentionally ignored as this is best-effort cleanup
_ = l.disableSystemdAutoStart(ctx) //nolint:errcheck
_ = l.disableXDGAutoStart() //nolint:errcheck
return nil
}

func (l *linuxPlatform) IsAutoStartEnabled(ctx context.Context) (bool, error) {
// Check systemd
if l.hasSystemd() {
if enabled, _ := l.isSystemdEnabled(ctx); enabled {
enabled, err := l.isSystemdEnabled(ctx)
if err == nil && enabled {
return true, nil
}
}
Expand Down Expand Up @@ -150,7 +160,10 @@ func (l *linuxPlatform) getSystemdUserDir() string {
if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
return filepath.Join(xdgConfig, "systemd", "user")
}
home, _ := os.UserHomeDir()
home, err := os.UserHomeDir()
if err != nil {
return filepath.Join(os.TempDir(), ".config", "systemd", "user")
}
return filepath.Join(home, ".config", "systemd", "user")
}

Expand Down Expand Up @@ -194,7 +207,10 @@ func (l *linuxPlatform) getXDGAutostartDir() string {
if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
return filepath.Join(xdgConfig, "autostart")
}
home, _ := os.UserHomeDir()
home, err := os.UserHomeDir()
if err != nil {
return filepath.Join(os.TempDir(), ".config", "autostart")
}
return filepath.Join(home, ".config", "autostart")
}

Expand Down Expand Up @@ -253,6 +269,7 @@ func (l *linuxPlatform) ShowNotification(title, message string) error {
}
// Try zenity
if _, err := exec.LookPath("zenity"); err == nil {
// #nosec G204 -- title and message are from controlled internal sources
return exec.Command("zenity", "--notification", "--text="+title+"\n"+message).Run()
}
return fmt.Errorf("no notification system available")
Expand All @@ -273,6 +290,7 @@ func (l *linuxPlatform) ShowChangelogDialog(agentName, fromVer, toVer, changelog
func (l *linuxPlatform) showZenityDialog(agentName, fromVer, toVer, changelog string) DialogResult {
text := fmt.Sprintf("%s\n\n%s → %s\n\n%s", agentName, fromVer, toVer, changelog)

// #nosec G204 -- arguments are from controlled catalog sources, not user input
cmd := exec.Command("zenity", "--question",
"--title=Update Available",
"--text="+text,
Expand Down
Loading