service

package
v0.0.0-...-5bdc5ce Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupOldLogs

func CleanupOldLogs(dir, name string, keep int)

CleanupOldLogs removes old log files for a service, keeping the most recent `keep` files. Log files are matched by the pattern <name>.*.log.

func IsAlive

func IsAlive(pid int) bool

IsAlive checks whether a process with the given PID exists. Returns true even if the process is owned by another user (EPERM).

func LogPath

func LogPath(dir, name string, pid int) string

LogPath returns the log file path for a service with a given PID.

func RemoveState

func RemoveState(dir, name string)

RemoveState deletes the state file.

func SaveState

func SaveState(dir, name string, s State) error

SaveState atomically writes the state to a JSON file. It writes to a temporary file first, then renames to prevent corruption.

func TailLogFile

func TailLogFile(path string, maxBytes int) string

TailLogFile reads the last maxBytes bytes from the file at path. Returns "" if the file doesn't exist or is empty.

func TruncateTailToRuneBudget

func TruncateTailToRuneBudget(s string, budget int) string

TruncateTailToRuneBudget returns s unchanged when its rune count fits within budget. Otherwise it keeps the tail of s (the last N runes) and prepends a marker line reporting how many bytes of earlier content were dropped. The returned string is always valid UTF-8 and its rune count is <= budget.

Callers are responsible for subtracting any surrounding scaffolding (prefix text, code fences, etc.) from the platform limit before passing budget in.

func VerifyProcess

func VerifyProcess(ps processBackendState) bool

VerifyProcess checks that the PID in the state still refers to the same process we originally started, by comparing boot time and process create time. If both identity fields are zero (e.g. gopsutil failed during start), returns true as graceful degradation -- we accept the PID rather than incorrectly restarting a process we can't identify.

Types

type Backend

type Backend interface {
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
	Restart(ctx context.Context) error
	Status(ctx context.Context) (string, error)
	Logs(ctx context.Context, tail int) (string, error)
	SaveBackendState() json.RawMessage
	RestoreBackendState(fullStateJSON json.RawMessage)
}

Backend manages the lifecycle of a service.

type ConfirmationTracker

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

ConfirmationTracker tracks pending deploy confirmations with expiry.

func NewConfirmationTracker

func NewConfirmationTracker(ttl time.Duration) *ConfirmationTracker

NewConfirmationTracker creates a ConfirmationTracker with the given TTL.

func (*ConfirmationTracker) AddPending

func (ct *ConfirmationTracker) AddPending(service, branch string)

AddPending registers a pending confirmation for a service. Overwrites any existing entry.

func (*ConfirmationTracker) Confirm

func (ct *ConfirmationTracker) Confirm(service string) bool

Confirm removes the pending confirmation for a service and returns true if it existed and had not expired. Returns false if no pending confirmation exists or it has expired.

func (*ConfirmationTracker) IsPending

func (ct *ConfirmationTracker) IsPending(service string) bool

IsPending reports whether there is a non-expired pending confirmation for the service. If the entry has expired it is deleted and false is returned.

type LaunchctlBackend

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

LaunchctlBackend manages macOS services via launchctl.

func NewLaunchctlBackend

func NewLaunchctlBackend(label string) *LaunchctlBackend

NewLaunchctlBackend returns a Backend that controls the given launchd service label.

func (*LaunchctlBackend) Logs

func (b *LaunchctlBackend) Logs(ctx context.Context, tail int) (string, error)

Logs returns recent log output for the service from the unified macOS log.

func (*LaunchctlBackend) Restart

func (b *LaunchctlBackend) Restart(ctx context.Context) error

Restart stops then starts the service.

func (*LaunchctlBackend) RestoreBackendState

func (b *LaunchctlBackend) RestoreBackendState(json.RawMessage)

RestoreBackendState is a no-op for launchctl.

func (*LaunchctlBackend) SaveBackendState

func (b *LaunchctlBackend) SaveBackendState() json.RawMessage

SaveBackendState returns nil (launchctl manages its own state).

func (*LaunchctlBackend) Start

func (b *LaunchctlBackend) Start(ctx context.Context) error

Start starts the service.

func (*LaunchctlBackend) Status

func (b *LaunchctlBackend) Status(ctx context.Context) (string, error)

Status returns "running" when the label appears in `launchctl list` output, and "stopped" otherwise.

func (*LaunchctlBackend) Stop

func (b *LaunchctlBackend) Stop(ctx context.Context) error

Stop stops the service.

type Manager

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

Manager manages a set of services.

func NewManager

func NewManager(cfg *config.Config, services []config.ServiceConfig, notifier Notifier) (*Manager, error)

NewManager creates a Manager for the given service configs.

func (*Manager) CountRunning

func (m *Manager) CountRunning() (int, int)

CountRunning returns (running, total) service counts.

func (*Manager) Do

func (m *Manager) Do(name, op string) string

Do sends a synchronous operation to the named service and blocks for the result.

func (*Manager) FindServiceByRepo

func (m *Manager) FindServiceByRepo(repo, branch string) (string, bool)

FindServiceByRepo returns the service name matching the given repo and branch. repo may be "org/name" or "github.com/org/name"; both are matched.

func (*Manager) GetAllStates

func (m *Manager) GetAllStates() map[string]ServiceState

GetAllStates returns a snapshot of all service states.

func (*Manager) GetServiceConfig

func (m *Manager) GetServiceConfig(name string) (config.ServiceConfig, bool)

GetServiceConfig returns the config for a named service.

func (*Manager) GetServiceLogs

func (m *Manager) GetServiceLogs(name string) string

GetServiceLogs returns the recent logs for a named service.

func (*Manager) GetServiceState

func (m *Manager) GetServiceState(name string) (ServiceState, bool)

GetServiceState returns the current state for a single named service.

func (*Manager) NotifyWebhook

func (m *Manager) NotifyWebhook(name string, info WebhookInfo)

NotifyWebhook forwards webhook details to the notifier, for a service that matched an incoming webhook.

func (*Manager) Reload

func (m *Manager) Reload() error

Reload re-reads services from the services directory and adds/removes/updates services as needed.

func (*Manager) RequestDeploy

func (m *Manager) RequestDeploy(name string) error

RequestDeploy queues a deploy request for the named service (latest-wins).

func (*Manager) ServiceNames

func (m *Manager) ServiceNames() []string

ServiceNames returns a sorted list of all service names.

func (*Manager) SetNotifier

func (m *Manager) SetNotifier(n Notifier)

SetNotifier sets or replaces the notifier. Must be called before deploys start to avoid races (the notifier is used from service loop goroutines).

func (*Manager) SetOnChange

func (m *Manager) SetOnChange(fn func(name, event string))

SetOnChange registers a callback invoked after service state transitions.

func (*Manager) ShutdownCh

func (m *Manager) ShutdownCh() <-chan struct{}

ShutdownCh returns a channel that is closed when a self-deploy succeeds, signalling the application to shut down gracefully.

func (*Manager) SignalReady

func (m *Manager) SignalReady()

SignalReady indicates that frontends are connected and notifications can be sent. Service loops waiting on restartOnStartup will proceed after this is called.

func (*Manager) StartAll

func (m *Manager) StartAll()

StartAll starts all services.

func (*Manager) Stop

func (m *Manager) Stop()

Stop cancels the manager context and waits for all service loops to exit.

func (*Manager) StopAll

func (m *Manager) StopAll()

StopAll stops all services.

type MultiNotifier

type MultiNotifier []Notifier

MultiNotifier fans out events to multiple notifiers.

func (MultiNotifier) DeployFailed

func (m MultiNotifier) DeployFailed(name, step, output string)

DeployFailed notifies all registered notifiers.

func (MultiNotifier) DeployStarted

func (m MultiNotifier) DeployStarted(name string)

DeployStarted notifies all registered notifiers.

func (MultiNotifier) DeploySucceeded

func (m MultiNotifier) DeploySucceeded(name, output string)

DeploySucceeded notifies all registered notifiers.

func (MultiNotifier) ServiceEvent

func (m MultiNotifier) ServiceEvent(name, event string)

ServiceEvent notifies all registered notifiers.

func (MultiNotifier) WebhookReceived

func (m MultiNotifier) WebhookReceived(name string, info WebhookInfo)

WebhookReceived notifies all registered notifiers.

type NopNotifier

type NopNotifier struct{}

NopNotifier discards all events. Useful as a default or in tests.

func (NopNotifier) DeployFailed

func (NopNotifier) DeployFailed(string, string, string)

func (NopNotifier) DeployStarted

func (NopNotifier) DeployStarted(string)

func (NopNotifier) DeploySucceeded

func (NopNotifier) DeploySucceeded(string, string)

func (NopNotifier) ServiceEvent

func (NopNotifier) ServiceEvent(string, string)

func (NopNotifier) WebhookReceived

func (NopNotifier) WebhookReceived(string, WebhookInfo)

type Notifier

type Notifier interface {
	ServiceEvent(name, event string)
	DeployStarted(name string)
	DeploySucceeded(name, output string)
	DeployFailed(name, step, output string)
	WebhookReceived(name string, info WebhookInfo)
}

Notifier receives service lifecycle and deploy events.

type ProcessBackend

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

ProcessBackend manages a service as a child process with process adoption and graceful shutdown (SIGTERM then SIGKILL after timeout).

func NewProcessBackend

func NewProcessBackend(name, dir string, entrypoint []string, cmd string, logDir string) *ProcessBackend

NewProcessBackend creates a new ProcessBackend.

func (*ProcessBackend) IsRunning

func (p *ProcessBackend) IsRunning() bool

IsRunning returns whether the process is currently running (thread-safe).

func (*ProcessBackend) Logs

func (p *ProcessBackend) Logs(ctx context.Context, tail int) (string, error)

Logs returns the tail of the current log file.

func (*ProcessBackend) Restart

func (p *ProcessBackend) Restart(ctx context.Context) error

Restart stops then starts the process.

func (*ProcessBackend) RestoreBackendState

func (p *ProcessBackend) RestoreBackendState(fullStateJSON json.RawMessage)

RestoreBackendState restores the backend-specific state from the full state file JSON. It handles both the new format (with a "backend" sub-object) and the old format (with pid/pgid/etc at the top level) for migration.

func (*ProcessBackend) SaveBackendState

func (p *ProcessBackend) SaveBackendState() json.RawMessage

SaveBackendState returns the backend-specific state as JSON. Returns nil if the process is not running.

func (*ProcessBackend) Start

func (p *ProcessBackend) Start(ctx context.Context) error

Start launches the process. If already running, returns nil.

func (*ProcessBackend) Status

func (p *ProcessBackend) Status(ctx context.Context) (string, error)

Status returns "running" or "stopped".

func (*ProcessBackend) Stop

func (p *ProcessBackend) Stop(ctx context.Context) error

Stop sends SIGTERM to the process group, waits up to 5 seconds, then sends SIGKILL. Returns nil if the process is not running.

func (*ProcessBackend) TryAdopt

func (p *ProcessBackend) TryAdopt() string

TryAdopt attempts to re-adopt a process from a previous session using the restored state. Returns a string describing what happened.

func (*ProcessBackend) WaitForExit

func (p *ProcessBackend) WaitForExit() <-chan struct{}

WaitForExit returns a channel that is closed when the running process exits. Returns nil if no process is running, which makes the channel effectively disabled in a select (a nil channel blocks forever).

type ServiceState

type ServiceState struct {
	Status      string    `json:"status"`
	LastDeploy  time.Time `json:"last_deploy,omitzero"`
	LastRestart time.Time `json:"last_restart,omitzero"`
	LastResult  string    `json:"last_result,omitempty"`
	LastOutput  string    `json:"last_output,omitempty"`
	FailedStep  string    `json:"failed_step,omitempty"`
}

ServiceState holds the current state of a managed service.

type State

type State struct {
	Status      string          `json:"status"`
	LastDeploy  time.Time       `json:"last_deploy,omitzero"`
	LastRestart time.Time       `json:"last_restart,omitzero"`
	LastResult  string          `json:"last_result,omitempty"`
	LastOutput  string          `json:"last_output,omitempty"`
	FailedStep  string          `json:"failed_step,omitempty"`
	Backend     json.RawMessage `json:"backend,omitempty"`
}

State represents the persisted state of a managed service.

func LoadState

func LoadState(dir, name string) (State, json.RawMessage, error)

LoadState reads the state from a JSON file. Returns the parsed State, the raw JSON bytes (for backend migration), and any error.

type SystemctlBackend

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

SystemctlBackend manages a systemd service unit via systemctl.

func NewSystemctlBackend

func NewSystemctlBackend(unit string, userMode bool, sudo bool) *SystemctlBackend

NewSystemctlBackend returns a Backend that controls the given systemd unit. If userMode is true, commands include the --user flag. If sudo is true, commands are run via sudo (for system services managed by a non-root user).

func (*SystemctlBackend) Logs

func (s *SystemctlBackend) Logs(ctx context.Context, tail int) (string, error)

Logs returns recent log output from journalctl.

func (*SystemctlBackend) Restart

func (s *SystemctlBackend) Restart(ctx context.Context) error

Restart restarts the service.

func (*SystemctlBackend) RestoreBackendState

func (s *SystemctlBackend) RestoreBackendState(json.RawMessage)

RestoreBackendState is a no-op for systemctl.

func (*SystemctlBackend) SaveBackendState

func (s *SystemctlBackend) SaveBackendState() json.RawMessage

SaveBackendState returns nil (systemctl manages its own state).

func (*SystemctlBackend) Start

func (s *SystemctlBackend) Start(ctx context.Context) error

Start starts the service.

func (*SystemctlBackend) Status

func (s *SystemctlBackend) Status(ctx context.Context) (string, error)

Status returns "running", "stopped", or "failed" based on systemctl is-active.

func (*SystemctlBackend) Stop

func (s *SystemctlBackend) Stop(ctx context.Context) error

Stop stops the service.

type WebhookInfo

type WebhookInfo struct {
	Repo      string
	Branch    string
	Compare   string
	Pusher    string
	CommitID  string
	CommitMsg string
	CommitURL string
	Author    string
	Timestamp string
}

WebhookInfo carries details about a received webhook for notifications.

func (WebhookInfo) FormatMessage

func (w WebhookInfo) FormatMessage(wrappedName string) string

FormatMessage builds a Markdown message describing this webhook event. wrappedName is the service name already wrapped in the target platform's code/bold syntax (e.g. "`svc`" for Mattermost, "**svc**" for Discord). The rest of the formatting (Markdown links, blockquotes, code spans) is compatible with both renderers.

func (WebhookInfo) ShortMessage

func (w WebhookInfo) ShortMessage() string

ShortMessage returns the first line of the commit message, truncated to 300 characters with "..." appended if truncation occurred.

func (WebhookInfo) ShortSHA

func (w WebhookInfo) ShortSHA() string

ShortSHA returns the first 7 characters of the commit SHA, or the full ID if it's shorter than 7 characters.

Jump to

Keyboard shortcuts

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