launch

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package launch provides application launching strategies for macOS app bundles.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewIOWrapper

func NewIOWrapper(dest io.Writer, stream string) io.Writer

NewIOWrapper creates a new IO wrapper based on environment configuration

Types

type Config

type Config struct {
	// AppName is the name of the application
	AppName string
	// BundleID is the bundle identifier
	BundleID string
	// Permissions are the requested macOS permissions (as strings)
	Permissions []string
	// Debug enables debug logging
	Debug bool
	// ForceLaunchServices forces use of LaunchServices
	ForceLaunchServices bool
	// ForceDirectExecution forces direct execution
	ForceDirectExecution bool
	// Background indicates the app should not steal focus (LSBackgroundOnly apps)
	Background bool
	// SingleProcess enables single-process mode via codesign + re-exec + setActivationPolicy.
	SingleProcess bool
	// Entitlements are the entitlement keys to sign the binary with (for transform mode).
	Entitlements []string
	// UIMode controls how the transformed app appears: "regular", "accessory", or "background".
	UIMode string
	// IconPath is the path to an .icns file for the Dock icon (transform mode, regular UI only).
	IconPath string
}

Config contains the launch-specific configuration extracted from the main Config. This avoids importing the main package and keeps the launch package focused.

type DirectLauncher

type DirectLauncher struct{}

DirectLauncher implements direct execution of the binary within the bundle.

func (*DirectLauncher) Launch

func (d *DirectLauncher) Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error

Launch executes the binary directly within the app bundle.

type IOWrapper

type IOWrapper struct {
	// contains filtered or unexported fields
}

IOWrapper wraps IO streams to add prefixes, indentation, or styling

func (*IOWrapper) Write

func (w *IOWrapper) Write(p []byte) (int, error)

Write implements io.Writer, adding prefix and styling to each line

type Launcher

type Launcher interface {
	// Launch executes the application using the appropriate strategy.
	Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error
}

Launcher defines the interface for launching applications.

type Logger

type Logger struct {
	*slog.Logger
}

Logger wraps slog.Logger with macgo-specific configuration

func NewLogger

func NewLogger() *Logger

NewLogger creates a configured logger based on environment variables

func (*Logger) Debug

func (l *Logger) Debug(msg string, args ...any)

Debug logs at debug level with "macgo:" prefix for compatibility

func (*Logger) Error

func (l *Logger) Error(msg string, args ...any)

Error logs at error level with "macgo:" prefix for compatibility

func (*Logger) Info

func (l *Logger) Info(msg string, args ...any)

Info logs at info level with "macgo:" prefix for compatibility

func (*Logger) Warn

func (l *Logger) Warn(msg string, args ...any)

Warn logs at warn level with "macgo:" prefix for compatibility

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager coordinates different launch strategies.

func New

func New() *Manager

New creates a new launch manager with default launchers.

func NewWithLaunchers

func NewWithLaunchers(direct, services Launcher) *Manager

NewWithLaunchers creates a new launch manager with custom launchers.

func (*Manager) Launch

func (m *Manager) Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error

Launch determines the appropriate strategy and launches the application.

type ServicesLauncher

type ServicesLauncher struct {
	// contains filtered or unexported fields
}

ServicesLauncher implements launching via LaunchServices using the 'open' command.

Signal Handling Architecture

When launching apps via LaunchServices, the child process is adopted by launchd (PPID=1) and is NOT in the parent's process group. This means terminal signals like Ctrl+C (SIGINT) only reach the parent wrapper, not the child.

To ensure proper signal handling, we implement the following:

  • PID Tracking: Child writes its PID to a control FIFO in the pipe directory after startup. Parent reads the PID (blocking, no polling) for signal forwarding.

  • Signal Forwarding: When parent receives SIGINT/SIGTERM (via context cancellation), it forwards these signals to the child before exiting. This prevents orphaned child processes.

  • SIGWINCH Forwarding: Terminal resize events are forwarded to the child so apps can adjust their output to the new terminal size.

  • Exit Codes: Parent exits with 130 (128+SIGINT) when interrupted by Ctrl+C. Child exits with 128+signal when terminated by a signal.

Note: SIGTSTP/SIGCONT (job control) is not currently supported. Pressing Ctrl+Z will suspend the parent but the child will continue running.

func (*ServicesLauncher) Launch

func (s *ServicesLauncher) Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error

Launch executes the application using LaunchServices with I/O forwarding.

type SingleProcessLauncher

type SingleProcessLauncher struct {
	// contains filtered or unexported fields
}

SingleProcessLauncher implements single-process mode via codesign + re-exec + setActivationPolicy.

Instead of creating a .app bundle, this launcher:

  1. Writes an entitlements plist to a temp file
  2. Codesigns the current binary in-place with the requested entitlements
  3. Re-execs itself so the kernel picks up the new code signature
  4. After re-exec, calls NSApplication setActivationPolicy to become a foreground app

IMPORTANT: syscall.Exec must happen BEFORE any NSApplication/GUI initialization. After GUI init, exec hangs due to stale Mach ports from the window server.

func (*SingleProcessLauncher) Launch

func (t *SingleProcessLauncher) Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error

Launch implements the Launcher interface for single-process mode.

The bundlePath argument is unused since no bundle is created. The execPath is the binary to codesign and re-exec.

type Strategy

type Strategy int

Strategy represents different ways to launch an application.

const (
	// StrategyDirect executes the binary directly within the bundle.
	StrategyDirect Strategy = iota
	// StrategyServices uses LaunchServices via the 'open' command.
	StrategyServices
	// StrategySingleProcess codesigns in-place, re-execs, and uses setActivationPolicy.
	StrategySingleProcess
)

func (Strategy) String

func (s Strategy) String() string

String returns a string representation of the strategy.

Jump to

Keyboard shortcuts

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