## Summary Adds `tools/hangar` — a macOS desktop control panel for working on Fleet locally: branch management, `fleet serve` orchestration, log tail, dev MySQL backup/restore, `fleetctl`, GitOps, and `osquery-perf`, all in one window. Built with **Go + [Wails 3](https://v3alpha.wails.io)** — the backend is plain Go (`os/exec`, `syscall`, goroutines) so Fleet engineers can contribute to it; only the desktop shell is Wails. The `internal/` packages are pure and unit-tested. ### History note Hangar started as a Rust/Tauri app. It was ported to Go, and **the Go port is now the canonical `tools/hangar`**. The original Rust/Tauri implementation has been removed from the monorepo (preserved in a standalone repo) — so although this branch's earlier commits add and then replace the Rust app, the net diff is just the Go app at `tools/hangar`. The bundle identifier is `com.fleetdm.fleet-hangar`, matching the original app so existing settings carry over. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops > No `changes/` file: `tools/` is contributor tooling, not a user-visible Fleet change. No DB migrations, no Fleet config settings, no fleetd/orbit changes. ## Testing - [x] Added/updated automated tests (Go unit tests across `internal/`, including a path-traversal regression for backup deletion) - [x] QA'd all new/changed functionality manually ## Test plan - [x] `cd tools/hangar && task dev` launches the app (live-reload) - [x] `task build` produces `bin/fleet-hangar`; `go test ./...` is green - [x] First-run gate discovers a local Fleet clone and runs dep checks - [x] Server tab can run the build chain and start `fleet serve` - [x] Git tab branch search finds an older branch (e.g. a stale `qa-*`) by name <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced Fleet Hangar, a comprehensive desktop application for Fleet development workflows, providing unified controls for server/database management, git operations, configuration, logging, and troubleshooting. * Added database backup management with metadata tracking. * Integrated process orchestration for development services (Docker, ngrok, Python). <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: George Karr <georgekarrv@users.noreply.github.com>
73 lines
2.4 KiB
Go
73 lines
2.4 KiB
Go
// Package services holds the Wails-bound service structs. Each method is
|
|
// callable from the frontend; the structs are thin adapters that resolve
|
|
// real paths and delegate to the internal/* packages (where the logic and
|
|
// its tests live).
|
|
package services
|
|
|
|
import (
|
|
"github.com/fleetdm/fleet/tools/hangar/internal/paths"
|
|
"github.com/fleetdm/fleet/tools/hangar/internal/settings"
|
|
)
|
|
|
|
// SettingsService exposes settings, repo probing, ngrok parsing, and the
|
|
// sandboxed file helpers. Mirrors the settings/* commands from settings.rs.
|
|
type SettingsService struct{}
|
|
|
|
// GetSettings loads the persisted settings (defaults if none saved yet).
|
|
func (s *SettingsService) GetSettings() (settings.Settings, error) {
|
|
dir, err := paths.ConfigDir()
|
|
if err != nil {
|
|
return settings.Settings{}, err
|
|
}
|
|
return settings.Load(dir)
|
|
}
|
|
|
|
// SaveSettings persists the given settings.
|
|
func (s *SettingsService) SaveSettings(in settings.Settings) error {
|
|
dir, err := paths.ConfigDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return settings.Save(dir, in)
|
|
}
|
|
|
|
// ProbeFleetRepo validates a single path, or (when path is empty) discovers
|
|
// Fleet clones under the well-known dev roots.
|
|
func (s *SettingsService) ProbeFleetRepo(path string) []settings.RepoProbe {
|
|
if path != "" {
|
|
return []settings.RepoProbe{settings.ProbeOne(path)}
|
|
}
|
|
return settings.DiscoverFleetRepos()
|
|
}
|
|
|
|
// DetectFleetConfig returns the relative serve-config name in the repo root
|
|
// (fleet.yml/fleet.yaml), or "" if none.
|
|
func (s *SettingsService) DetectFleetConfig(repo string) string {
|
|
return settings.DetectFleetConfig(repo)
|
|
}
|
|
|
|
// ParseNgrokYml summarizes an ngrok.yml (empty path = ngrok's default).
|
|
func (s *SettingsService) ParseNgrokYml(path string) settings.NgrokYamlInfo {
|
|
return settings.ParseNgrokYml(path)
|
|
}
|
|
|
|
// ReadTextFile reads a .yml/.yaml file under $HOME.
|
|
func (s *SettingsService) ReadTextFile(path string) (string, error) {
|
|
return settings.ReadTextFile(path)
|
|
}
|
|
|
|
// WriteTextFile writes a .yml/.yaml file under $HOME.
|
|
func (s *SettingsService) WriteTextFile(path, contents string) error {
|
|
return settings.WriteTextFile(path, contents)
|
|
}
|
|
|
|
// OpenPath opens a dir or allowed file in the system file manager.
|
|
func (s *SettingsService) OpenPath(path string, reveal bool) error {
|
|
return settings.OpenPath(path, reveal)
|
|
}
|
|
|
|
// OpenURL opens an http(s) URL in the default browser.
|
|
func (s *SettingsService) OpenURL(url string) error {
|
|
return settings.OpenURL(url)
|
|
}
|