startup

package
v0.0.0-...-15a56fc Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package startup manages user-scoped autostart entries created by gitmap on the host OS:

  • Linux/Unix: XDG `.desktop` files in `$XDG_CONFIG_HOME/autostart/` (or `$HOME/.config/autostart/`) carrying the `X-Gitmap-Managed=true` key.
  • macOS: LaunchAgent `.plist` files in `~/Library/LaunchAgents/` carrying a top-level `<key>XGitmapManaged</key><true/>` marker.

On both OSes, List enumerates ONLY entries that satisfy BOTH the filename prefix gate AND the in-file marker; Remove deletes one named entry only after re-confirming it carries the marker. A request to remove a third-party entry becomes a refused no-op, never a deletion.

Windows is intentionally NOT covered by this package — Windows uses Registry `Run` keys / Startup folder shortcuts handled by separate code. The directory resolver returns an error on Windows so the CLI prints the "unsupported OS" message instead of touching a non-existent directory.

macOS LaunchAgent lifecycle (launchctl load/unload) is NOT triggered here — list/remove operate on the .plist file ONLY. A removed plist takes effect at the next login or after a manual `launchctl unload`. This is intentional: invoking launchctl requires a running user GUI session and would make automated uninstall scripts brittle on CI / SSH sessions.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AutostartDir

func AutostartDir() (string, error)

AutostartDir returns the absolute path to the user's autostart directory for the current OS.

  • Linux/Unix: honors $XDG_CONFIG_HOME, falls back to $HOME/.config/autostart per freedesktop.org base-dir spec.
  • macOS: $HOME/Library/LaunchAgents (per Apple's LaunchAgents documentation; the user-domain location requires no sudo).

Returns an error on Windows so callers print the platform-specific "unsupported OS" message instead of touching a non-existent dir.

Types

type AddOptions

type AddOptions struct {
	// Name is the entry's logical identifier. The on-disk filename
	// becomes "gitmap-<Name>.desktop" (prefix added if missing) and
	// the listed Name= field in the .desktop body is the same value.
	Name string
	// Exec is the absolute command line to launch at login. Callers
	// MUST pre-quote any path containing spaces (the .desktop spec
	// allows quoted strings inside Exec=); we do not parse it.
	Exec string
	// DisplayName, when non-empty, overrides the value written to
	// the Name= field. Empty means "reuse Name verbatim".
	DisplayName string
	// Comment populates the Comment= field. Optional.
	Comment string
	// NoDisplay sets NoDisplay=true to hide the entry from desktop
	// app menus while still autostarting it — useful for headless
	// helpers users don't want cluttering their app drawer.
	NoDisplay bool
	// Force allows overwriting a previously gitmap-created entry
	// with the same name. Has NO effect on third-party files; those
	// always refuse.
	Force bool
	// WorkingDir, when non-empty, sets the process working directory
	// for the autostart entry. Rendered as `Path=<dir>` in .desktop
	// files (XDG spec field), `WorkingDirectory` in LaunchAgent
	// plists, and stored as the `WorkingDir` value of the gitmap
	// tracking subkey on Windows (HKCU\Software\Gitmap\Startup*\<name>).
	// Callers MUST pass an absolute path; relative paths are accepted
	// as-is and interpreted by the OS at login time. Empty means
	// "inherit whatever the login session provides".
	WorkingDir string
	// Backend selects the Windows autostart target (Registry vs
	// Startup-folder shortcut). Ignored on Linux/macOS, which each
	// have one canonical backend. Zero value (BackendUnspecified)
	// means "use the OS default" which is BackendRegistry on
	// Windows.
	Backend Backend
}

AddOptions captures every knob the runner exposes. Kept as a struct (not positional args) so future fields like Comment, Categories, OnlyShowIn don't keep growing the signature.

type AddResult

type AddResult struct {
	Status AddStatus
	Path   string
}

AddResult mirrors RemoveResult. Path is the absolute target file for Created / Overwritten / Refused / Exists; empty for BadName.

func Add

func Add(opts AddOptions) (AddResult, error)

Add is the public entry point. Returns (result, nil) for every "soft" outcome; only real I/O failures produce a non-nil error.

OS dispatch:

  • linux/unix → writes a `.desktop` file with the X-Gitmap-Managed=true marker into AutostartDir().
  • darwin → writes a LaunchAgent `.plist` with the XGitmapManaged <true/> marker into ~/Library/LaunchAgents/.
  • windows → routes via opts.Backend (Registry by default, or Startup-folder .lnk shortcut). Both backends share the same managed-marker contract enforced by HKCU\Software\Gitmap tracking subkeys + a sibling marker value next to Run-key entries. See addWindows / winbackend.go for details.

Both OS paths share the same five-status outcome model (Created/Overwritten/Refused/BadName/Exists) and the same "managed-only, never escalate" guard — Force only lifts the "already exists AND is ours" check; a third-party file is NEVER overwritten.

type AddStatus

type AddStatus int

AddStatus tags the four mutually-exclusive Add outcomes. Kept parallel to RemoveStatus so the CLI rendering layer can switch on either with the same shape.

const (
	// AddCreated = file did not exist; was written fresh.
	AddCreated AddStatus = iota
	// AddOverwritten = previously gitmap-managed file was replaced
	// because Force was set.
	AddOverwritten
	// AddRefused = a non-gitmap-managed file with the same name
	// already exists; we did NOT touch it.
	AddRefused
	// AddBadName = name failed validation (empty / separator / NUL).
	AddBadName
	// AddExists = a gitmap-managed file with the same name already
	// exists and Force was NOT set; nothing was written.
	AddExists
)

type Backend

type Backend int

Backend enumerates the Windows-specific add targets. Linux/macOS have one canonical backend each (XDG .desktop / LaunchAgents .plist) so they ignore this value entirely. The zero value (BackendUnspecified) means "let the dispatcher pick the OS default" which is `BackendRegistry` on Windows.

const (
	// BackendUnspecified is the zero value. The dispatcher
	// translates it to the per-OS default rather than failing —
	// keeps the public Add(opts) API ergonomic for non-Windows
	// callers that have no opinion on backend.
	BackendUnspecified Backend = iota
	// BackendRegistry writes the HKCU Run-key value + tracking
	// subkey (per-user; the long-standing default).
	BackendRegistry
	// BackendStartupFolder writes the .lnk + tracking subkey.
	BackendStartupFolder
	// BackendRegistryHKLM writes the HKLM Run-key value +
	// tracking subkey (machine-wide; requires admin). Same on-
	// disk shape as BackendRegistry, just rooted under
	// HKEY_LOCAL_MACHINE so every interactive user on the
	// machine triggers the autostart at login.
	BackendRegistryHKLM
)

func ParseBackend

func ParseBackend(s string) (Backend, error)

ParseBackend translates the user-facing flag string into the Backend enum. Unknown / empty values from non-Windows callers flow through as BackendUnspecified (the per-OS default); empty values from Windows callers also default to Registry. ONLY a non-empty unrecognized value is an error — that means the user typed something we don't understand and silently defaulting would hide the typo.

func (Backend) String

func (b Backend) String() string

String renders the backend as the canonical CLI flag value. Used by list/remove rendering to label which backend an entry lives in.

type Entry

type Entry struct {
	Name string
	Path string
	Exec string
}

Entry is one gitmap-managed autostart record. Path is the absolute file path; Name is the basename WITHOUT the platform-specific extension (the form users pass to `startup-remove`); Exec is the command line surfaced so `startup-list` shows what would actually run at login. On macOS, Exec is the space-joined ProgramArguments (or the Program string if ProgramArguments is absent).

func List

func List() ([]Entry, error)

List returns every gitmap-managed entry. A MISSING directory / registry key is treated as "zero entries", NOT an error — fresh accounts that have never had any autostart entry shouldn't see a scary error from `gitmap startup-list`. On Windows, enumerates BOTH the Registry Run-key and Startup-folder backends.

type RemoveOptions

type RemoveOptions struct {
	// DryRun runs the full classification (existence + marker check)
	// but skips the actual os.Remove call. The returned RemoveResult
	// has DryRun=true and the Status the live call would have
	// produced — letting CLI renderers print "would delete X" with
	// the same accuracy as a real run.
	DryRun bool
	// Backend scopes the removal to a specific Windows backend
	// (registry or startup-folder). Zero value (BackendUnspecified)
	// preserves legacy behavior: both backends are tried in order
	// and the first non-NoOp result wins. Linux and macOS callers
	// ignore this field — there's only one backend per OS.
	Backend Backend
}

RemoveOptions carries optional knobs for Remove. Kept as a struct (not extra positional args) so future flags like Trash bool or BackupTo string can be added without breaking callers.

type RemoveResult

type RemoveResult struct {
	Status RemoveStatus
	Path   string
	DryRun bool
}

RemoveResult tags every Remove outcome so callers can render the right user-facing message without inspecting error text. The path is empty for NotOurs / NoOp / BadName so callers don't accidentally print a path the user can't act on. DryRun reports whether the outcome was simulated (no filesystem mutation) — set when the caller passed RemoveOptions.DryRun=true and the status would otherwise have been RemoveDeleted.

func Remove

func Remove(name string) (RemoveResult, error)

Remove deletes the named gitmap-managed autostart entry. `name` is the basename WITHOUT the platform extension (the same form `List` returns); a trailing platform extension is tolerated so users who copy/paste from `ls` get the same behavior. The extension is `.desktop` on Linux/Unix and `.plist` on macOS.

This is the legacy entry point — equivalent to RemoveWithOptions(name, RemoveOptions{}). New callers that need dry-run semantics should call RemoveWithOptions directly.

func RemoveWithOptions

func RemoveWithOptions(name string, opts RemoveOptions) (RemoveResult, error)

RemoveWithOptions is the full-featured entry point. The dry-run branch reuses every classification check the live branch runs so `--dry-run` cannot disagree with the real command — only the final os.Remove is suppressed.

type RemoveStatus

type RemoveStatus int

RemoveStatus enumerates the four mutually-exclusive outcomes.

const (
	// RemoveDeleted = file existed, was gitmap-managed, was unlinked
	// (or, under DryRun, would have been unlinked).
	RemoveDeleted RemoveStatus = iota
	// RemoveNoOp = no file by that name in the autostart dir.
	RemoveNoOp
	// RemoveRefused = file exists but is not gitmap-managed.
	RemoveRefused
	// RemoveBadName = input failed validation (empty / separator).
	RemoveBadName
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL