brun

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2025 License: MIT Imports: 19 Imported by: 0

README

BRun

 ____  ____
| __ )|  _ \ _   _ _ __
|  _ \| |_) | | | | '_ \
| |_) |  _ <| |_| | | | |
|____/|_| \_\\__,_|_| |_|

~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
    Trigger -> Run
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

BRun is a tool to run automated builds/tests with a focus on Linux bare-os (no containers or dependencies) testing. Features/goals:

  • simple!!!
  • fast!!!
  • 📦 no dependencies -- just a single binary and go
  • 🛠️ built-in commands for common tasks like boot, cron, email, git, file watching
  • 🔗 composed of chainable units
  • 💻 first priority is to run native
  • 🚫 does not require containers (but may support them in the future)
  • 📄 simple YAML config format

Example Configuration

Here's a complete example showing all supported unit types:

konfig:
  state_location: /var/lib/brun/state.yaml

units:
  # Start trigger - fires every time brun runs
  - start:
      name: on-start
      on_success:
        - build

  # Boot trigger - fires once per boot cycle
  - boot:
      name: on-boot
      on_success:
        - build
        - test
      always:
        - log-boot

  # Run unit - executes shell commands/scripts
  - run:
      name: build
      directory: /home/user/project
      script: |
        echo "Building project..."
        go build -o brun ./cmd/brun
        echo "Build complete"
      on_success:
        - test
      on_failure:
        - log-build-error

  # Run unit - run tests
  - run:
      name: test
      script: |
        echo "Running tests..."
        go test -v
      on_success:
        - log-success
      on_failure:
        - log-test-error

  # Log unit - write to log files
  - log:
      name: log-boot
      file: /var/log/brun/boot.log

  - log:
      name: log-success
      file: /var/log/brun/success.log

  - log:
      name: log-build-error
      file: /var/log/brun/build-errors.log

  - log:
      name: log-test-error
      file: /var/log/brun/test-errors.log

  # Count unit - track how many times units trigger
  - count:
      name: build-counter

  # Cron trigger - runs every 5 minutes (useful in daemon mode)
  - cron:
      name: periodic-check
      schedule: "*/5 * * * *"
      on_success:
        - test

  # File trigger - monitors source files for changes (daemon mode)
  - file:
      name: watch-files
      pattern: "**/*.go"
      on_success:
        - build
        - test

  # Git trigger - monitors repository for changes
  - git:
      name: watch-repo
      repository: /home/user/project
      on_success:
        - build

  # Email unit - send notifications
  - email:
      name: email-failure
      to:
        - admin@example.com
      from: brun@example.com
      subject: "Build/Test Failure"
      smtp_host: smtp.gmail.com
      smtp_port: 587
      smtp_user: brun@example.com
      smtp_password: your-app-password
      smtp_use_tls: true
      include_output: true

  # Reboot unit - reboot the system (for reboot cycle testing)
  - reboot:
      name: reboot-system
      delay: 5

Install

To install, download the latest release and then run brun install.

If this is run as root, it installs a systemd service that runs as root, otherwise as the user that runs the install.

If a config file does not exist, one is created.

Usage

Usage: ./brun <command> [args]

Commands:
  run <config-file> [-daemon]    Run brun with the given config file
                                  -daemon: run in daemon mode (continuous monitoring)
                                  -unit <unit name>: run a single unit (useful for debugging)
                                   Triggers are not executed.
                                  -trigger <unit name>: trigger the named unit and execute
                                   triggers.
  install                        Install brun as a systemd service

One-time run:

By default, BRun runs once, checks all trigger conditions, executes any units whose conditions are met, and then exits. This is suitable for:

  • Running from external cron
  • Manual invocation
  • Testing configurations
brun run config.yaml

Daemon mode:

BRun supports a daemon mode that continuously monitors trigger conditions and executes units when triggered. In this mode, triggers are checked every 10 seconds. This is suitable for:

  • System service deployment
  • Continuous monitoring with cron triggers
  • Long-running background processes
brun run config.yaml -daemon

Circular Dependency Protection

BRun protects against circular dependencies when units trigger each other. For example, if Unit A triggers Unit B, and Unit B triggers Unit A, this could cause an infinite loop.

How it works:

  • The orchestrator maintains a results map that tracks which units have executed in the current trigger cycle
  • Before executing a unit, the orchestrator checks if it has already run in this cycle
  • If a unit has already executed, it is skipped to prevent circular dependencies
  • At the start of each trigger cycle (every 10 seconds in daemon mode), the results map is cleared, allowing units to run again in the next cycle

This approach allows:

  • Periodic triggers to work correctly: Units can be triggered multiple times across different cycles (e.g., cron triggers firing every minute)
  • Circular dependency protection: Within a single trigger cycle, units cannot trigger each other recursively

Example:

units:
  - cron:
      name: every-minute
      schedule: "* * * * *"
      on_success:
        - task-a

  - run:
      name: task-a
      script: echo "Task A"
      always:
        - task-b

  - run:
      name: task-b
      script: echo "Task B"
      always:
        - task-a # This would create a circular dependency

In this example:

  • The cron trigger fires every minute and triggers task-a
  • task-a triggers task-b
  • task-b attempts to trigger task-a, but it's already in the results map
  • The circular trigger is prevented, and the log shows: "Unit 'task-a' already executed in this chain, skipping to prevent circular dependency"
  • In the next minute, the results map is cleared and the cycle can run again

Logging

By default, logging is sent to STDOUT, and each unit logs:

  • when it triggers or runs
  • any errors

Additional log units can log specific events.

State

BRun uses a single common state file (YAML format) where all units store state between runs. This unified approach simplifies state management and makes it easy to:

  • Track all unit state in one location
  • Back up and restore state atomically
  • Clear all state with a single file deletion
  • Inspect and debug state using standard YAML tools

The state file location must be set in the BRun config file.

State Data:

Units store different types of state information in the YAML file:

  • Boot trigger: Last boot time (RFC3339 timestamp) and boot count
  • Cron trigger: Last execution time (RFC3339 timestamp)
  • Count unit: Trigger counts per triggering unit
  • File trigger: File hashes for change detection
  • Git trigger: Last processed commit hash

State File Format:

The state file uses YAML format for consistency with the configuration file. Each unit stores its state under a key corresponding to its name or type.

The state file is automatically created with appropriate permissions (0644) when BRun runs for the first time.

File format

YAML is used for the BRun file format and leverages the best of Gitlab CI/CD, Drone, Ansible, and other popular systems.

The system is composed of units. Each unit can trigger additional units. This allows us to start/sequence operations and create build/test pipelines.

Config

The BRun file consists of a required config section with the following fields:

config:
  state_location: /var/lib/brun/state.yaml

Fields:

  • state_location (required): Path to the state file where units store their state between runs.
    • Defaults to /var/lib/brun/state.yaml for root installs
    • Defaults to ~/.config/brun/state.yaml for user installs

The config file also contains a units section as described below.

Units

Common Unit Fields

All units share the following common fields:

  • name (required): A unique identifier for the unit. This name is used to reference the unit when triggering it from other units.
  • on_success (optional): An array of unit names to trigger when this unit completes successfully.
  • on_failure (optional): An array of unit names to trigger when this unit fails.
  • always (optional): An array of unit names to trigger regardless of whether this unit succeeds or fails. These units run after success/failure triggers.
Start Unit

The Start trigger always fires when brun runs. This can be used to trigger other units every time the program executes, regardless of boot state or other conditions.

Behavior:

  • Always triggers on every brun run
  • Does not maintain any state
  • Useful for unconditional execution pipelines

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - start:
      name: start-trigger
      on_success:
        - build-unit
        - test-unit
Boot Unit

The boot unit triggers if this is the first time the program has been run since the system booted. The boot unit stores the last boot time in the common state file.

Behavior:

The boot trigger detects boot events by:

  1. Reading /proc/uptime to calculate the system boot time
  2. Comparing this with a stored boot time from the previous run (saved in the common state file)
  3. Triggering when the boot times differ by more than 10 seconds

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - boot:
      name: boot-trigger
      on_success:
        - build-unit
        - test-unit

When the boot trigger fires successfully, it will trigger the units listed in on_success (in this example, build-unit and test-unit).

The boot time is automatically stored in the common state file under the unit's name.

Run Unit

The Run unit executes arbitrary shell commands or scripts. This is the primary execution unit for running builds, tests, or any other commands. The exit code determines success or failure, which then triggers the appropriate units.

Multiple Run units can be defined in a configuration file to create build and test pipelines.

Fields:

  • script (required): Shell commands to execute. Can be a single command or a multi-line script
  • directory (optional): Working directory where the script will be executed. Defaults to the directory where brun was invoked
  • timeout (optional): timeout duration for the task to complete (e.g., "30s", "5m", "1h", "1h30m"). If no timeout is specified, it runs until completion. If the task times out, an error message is logged.

Behavior:

  • The script is executed using the system shell
  • Exit code 0 is considered success and triggers on_success units
  • Non-zero exit codes are considered failures and trigger on_failure units
  • Both stdout and stderr are logged

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - boot:
      name: boot-trigger
      on_success:
        - build

  - run:
      name: build
      directory: /home/user/project
      script: |
        go build -o brun ./cmd/brun
        go test -v
      on_success:
        - deploy
      on_failure:
        - notify-failure

  - run:
      name: deploy
      script: |
        ./deploy.sh
Log Unit

The Log unit writes log entries to a file. This is useful for recording events, errors, or other information during pipeline execution. The log file is created if it doesn't exist, and entries are appended with timestamps.

Fields:

  • file (required): Path to the log file where entries will be written

Behavior:

  • Creates the log file and parent directories if they don't exist
  • Appends log entries with timestamps
  • File permissions are set to 0644
  • Directory permissions are set to 0755

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - start:
      name: start-trigger
      on_success:
        - build
      always:
        - log-run

  - run:
      name: build
      script: |
        go build -o brun ./cmd/brun
      on_failure:
        - log-error

  - log:
      name: log-run
      file: /var/log/brun/pipeline.log

  - log:
      name: log-error
      file: /var/log/brun/errors.log
Count Unit

The Count unit creates an entry in the state file for every unit that triggers this unit and counts how many times it has been triggered. This is useful for tracking how often specific events occur or how many times particular units execute.

Behavior:

  • Tracks separate counts for each unit that triggers it
  • Stores counts in the state file under the count unit's name
  • Each triggering unit has its own counter
  • Counts persist across runs

State File Format:

The count unit stores data in the state file like this:

count-runs:
  start-trigger: 5

count-builds:
  build: 3

count-failures:
  build: 1

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - start:
      name: start-trigger
      on_success:
        - build
      always:
        - count-runs

  - run:
      name: build
      script: |
        go build -o brun ./cmd/brun
      on_success:
        - count-builds
      on_failure:
        - count-failures

  - count:
      name: count-runs

  - count:
      name: count-builds

  - count:
      name: count-failures
Cron Unit

The Cron unit is a trigger that fires based on a cron schedule. It uses the standard cron format to define when the trigger should activate. In daemon mode, the trigger is checked every 10 seconds. The robfig/cron package is used for schedule parsing.

Fields:

  • schedule (required): Cron schedule in standard format (minute hour day month weekday)

Behavior:

  • Triggers based on the cron schedule
  • Stores last execution time in the state file
  • Works in both one-time and daemon modes
  • In one-time mode: triggers if schedule indicates it should have run since last execution
  • In daemon mode: continuously monitors and triggers at scheduled times

Cron Schedule Format:

Standard 5-field cron format:

* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)

Examples:

  • * * * * * - Every minute
  • */5 * * * * - Every 5 minutes
  • 0 2 * * * - Daily at 2:00 AM
  • 30 14 * * 1-5 - Weekdays at 2:30 PM
  • 0 0 1 * * - First day of every month at midnight

State File Format:

The cron unit stores the last execution time:

daily-backup:
  last_execution: "2025-10-03T02:30:00-04:00"

health-check:
  last_execution: "2025-10-03T18:00:00-04:00"

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  # Cron trigger - runs every day at 2:30 AM
  - cron:
      name: daily-backup
      schedule: "30 2 * * *"
      on_success:
        - backup-unit

  # Cron trigger - runs every 5 minutes
  - cron:
      name: health-check
      schedule: "*/5 * * * *"
      on_success:
        - check-services

  - run:
      name: backup-unit
      script: |
        echo "Running daily backup..."
        # backup commands here

  - run:
      name: check-services
      script: |
        echo "Checking services..."
        # health check commands here
File Unit

The File unit monitors files and triggers when they are changed. Files can be specified using glob patterns with support for ** recursive matching. New or removed files are detected as changes.

Fields:

  • pattern (required): Glob pattern to match files (supports ** for recursive matching)

Behavior:

  • Monitors files matching the glob pattern
  • Triggers when file content changes (detected via SHA256 hash)
  • Triggers when files are added or removed
  • Stores file hashes in the state file
  • Triggers on first run (initial file state)
  • Ignores directories (only monitors regular files)
  • Uses doublestar library for recursive glob support
  • Works in both one-time and daemon modes

Pattern Syntax:

The file unit supports advanced glob patterns including:

  • * - matches any sequence of non-separator characters
  • ? - matches any single non-separator character
  • [abc] - matches any character in the set
  • [a-z] - matches any character in the range
  • ** - matches zero or more directories recursively

Pattern Examples

  • */.go - all Go files recursively
  • src/*/.ts - all TypeScript files under src/
  • config/*.yaml - config files non-recursively
  • */.{html,css,js} - multiple file types

State File Format:

The file unit stores a hash of all monitored files:

watch-source:
  files_state: "file1.go:a1b2c3...|file2.go:d4e5f6..."

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  # File trigger - monitors Go source files
  - file:
      name: watch-source
      pattern: "**/*.go"
      on_success:
        - build
        - test

  - run:
      name: build
      script: |
        echo "Building..."
        go build -o app ./cmd/app

  - run:
      name: test
      script: |
        echo "Running tests..."
        go test -v ./...

Common patterns:

# Watch all Go files recursively
- file:
    name: watch-go
    pattern: "**/*.go"

# Watch configuration files
- file:
    name: watch-config
    pattern: "config/*.yaml"

# Watch specific directory non-recursively
- file:
    name: watch-src
    pattern: "src/*.js"

# Watch multiple file types
- file:
    name: watch-web
    pattern: "**/*.{html,css,js}"

Daemon mode example:

When running in daemon mode, the file trigger continuously monitors files and automatically triggers builds/tests when changes are detected:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - file:
      name: auto-build
      pattern: "**/*.go"
      on_success:
        - build
        - test
      always:
        - email-notify

  - run:
      name: build
      script: |
        go build -o app ./cmd/app

  - run:
      name: test
      script: |
        go test -v ./...

  - email:
      name: email-notify
      to:
        - team@example.com
      from: ci@example.com
      subject_prefix: "Build Status"
      smtp_host: smtp.example.com
      smtp_port: 587
      smtp_user: ci@example.com
      smtp_password: secret

Run with: brun run config.yaml -daemon

This creates a continuous integration system that automatically builds and tests your code whenever source files are modified.

Git Unit

The Git unit is a trigger that fires when changes are detected in a Git repository. It monitors the repository's HEAD commit and triggers when new commits are detected. This is useful for automatically running builds, tests, or deployments when code changes.

Fields:

  • repository (required): Path to the Git repository to monitor

Behavior:

  • Monitors the HEAD commit hash of the specified Git repository
  • Triggers when the commit hash changes (new commits detected)
  • Stores the last seen commit hash in the state file
  • Triggers on first run (initial repository state)
  • Uses go-git library (no git CLI tool required)
  • Works in both one-time and daemon modes

State File Format:

The git unit stores the last seen commit hash:

watch-repo:
  last_commit_hash: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"

Configuration example:

When running in daemon mode, the git trigger continuously monitors the repository and automatically triggers builds/tests when new commits are detected:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - git:
      name: auto-build
      repository: /home/user/project
      on_success:
        - build

  - run:
      name: build
      directory: /home/user/project
      script: |
        go build -o app ./cmd/app
        go test -v ./...
      always:
        - email

  - email:
      name: email
      to:
        - team@example.com
      from: ci@example.com
      subject_prefix: "Build Success"
      smtp_host: smtp.example.com
      smtp_port: 587
      smtp_user: ci@example.com
      smtp_password: secret

This creates a continuous integration system that automatically builds and tests your code whenever changes are pushed to the repository.

Email Unit

The Email unit sends email notifications with optional output from triggering units. This is useful for alerting on build failures, test results, or other important events. Supports both plain SMTP and STARTTLS encryption.

Fields:

  • to (required): Array of email addresses to send to
  • from (required): Sender email address
  • subject_prefix (optional): Email subject line prefix. ': :<success|fail>' is appended after prefix and is always included.
  • smtp_host (required): SMTP server hostname
  • smtp_port (optional): SMTP server port. Defaults to 587 (submission port)
  • smtp_user (optional): SMTP username for authentication
  • smtp_password (optional): SMTP password for authentication
  • smtp_use_tls (optional): Enable STARTTLS encryption. Defaults to true
  • include_output (optional): Include captured output from triggering unit. Defaults to true

Behavior:

  • Sends plain text emails using SMTP
  • Can include output from the unit that triggered it (useful for log/error reporting)
  • Supports SMTP authentication
  • STARTTLS encryption enabled by default
  • Works with common email providers (Gmail, SendGrid, Mailgun, etc.)

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - boot:
      name: boot-trigger
      on_success:
        - build

  - run:
      name: build
      script: |
        go build -o brun ./cmd/brun
        go test -v
      on_failure:
        - email-failure

  - email:
      name: email-failure
      to:
        - admin@example.com
        - alerts@example.com
      from: brun@example.com
      subject_prefix: "Build Alert"
      smtp_host: smtp.gmail.com
      smtp_port: 587
      smtp_user: brun@example.com
      smtp_password: your-app-password
      smtp_use_tls: true
      include_output: true

This will send emails with subjects like:

  • Build Alert: build:success (on success)
  • Build Alert: build:fail (on failure)

Gmail example:

For Gmail, you need to use an app-specific password:

- email:
    name: notify-admin
    to:
      - you@gmail.com
    from: your-app@gmail.com
    subject_prefix: "CI/CD"
    smtp_host: smtp.gmail.com
    smtp_port: 587
    smtp_user: your-app@gmail.com
    smtp_password: your-16-char-app-password
    smtp_use_tls: true
Email Receive Unit (TODO)

This can receive emails to trigger units.

Reboot Unit

The reboot unit logs and reboots the system. This is typically used in reboot cycle testing where the boot trigger can count boot cycles and trigger test sequences.

Fields:

  • delay (optional): Number of seconds to wait before executing reboot (default: 0 for immediate reboot)

Configuration example:

config:
  state_location: /var/lib/brun/state.yaml

units:
  - reboot:
      name: reboot-system
      delay: 5 # optional delay in seconds before reboot (default: 0)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Install

func Install() error

Install installs brun as a systemd service If run as root, installs system-wide service Otherwise, installs user service

Types

type BootConfig

type BootConfig struct {
	UnitConfig `yaml:",inline"`
}

BootConfig represents the configuration for a boot trigger

type BootDetector

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

BootDetector detects system boot time and tracks whether this is the first run since boot

func NewBootDetector

func NewBootDetector(stateFile string) *BootDetector

NewBootDetector creates a new boot detector with the given state file path

func (*BootDetector) GetBootTime

func (bd *BootDetector) GetBootTime() (time.Time, error)

GetBootTime returns the system boot time by reading /proc/uptime

func (*BootDetector) IsFirstRunSinceBoot

func (bd *BootDetector) IsFirstRunSinceBoot() (bool, error)

IsFirstRunSinceBoot checks if this is the first run since system boot It compares the current boot time with the stored boot time from the last run

type BootTrigger

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

BootTrigger is a trigger unit that fires on the first run after system boot

func NewBootTrigger

func NewBootTrigger(name string, state *State, onSuccess, onFailure, always []string) *BootTrigger

NewBootTrigger creates a new boot trigger unit

func (*BootTrigger) Always

func (s *BootTrigger) Always() []string

Always returns the list of units to trigger regardless of success/failure

func (*BootTrigger) Check

func (s *BootTrigger) Check(ctx context.Context) (bool, error)

Check returns true if this is the first run since system boot

func (*BootTrigger) Name

func (s *BootTrigger) Name() string

Name returns the name of the unit

func (*BootTrigger) OnFailure

func (s *BootTrigger) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*BootTrigger) OnSuccess

func (s *BootTrigger) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*BootTrigger) Run

func (s *BootTrigger) Run(ctx context.Context) error

Run executes the trigger unit

func (*BootTrigger) Type

func (s *BootTrigger) Type() string

Type returns the unit type

type Config

type Config struct {
	ConfigBlock ConfigBlock         `yaml:"config"`
	Units       []UnitConfigWrapper `yaml:"units"`
}

Config represents the SimplCI configuration file

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig loads a configuration file from the given path

func (*Config) CreateUnits

func (c *Config) CreateUnits() ([]Unit, error)

CreateUnits creates unit instances from the configuration

type ConfigBlock

type ConfigBlock struct {
	StateLocation string `yaml:"state_location"`
}

ConfigBlock represents the config section of the configuration file

type CountConfig

type CountConfig struct {
	UnitConfig `yaml:",inline"`
}

CountConfig represents the configuration for a Count unit

type CountUnit

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

CountUnit tracks how many times it has been triggered by each unit

func NewCountUnit

func NewCountUnit(name string, state *State, onSuccess, onFailure, always []string) *CountUnit

NewCountUnit creates a new Count unit

func (*CountUnit) Always

func (c *CountUnit) Always() []string

Always returns the list of units to always trigger

func (*CountUnit) Name

func (c *CountUnit) Name() string

Name returns the unit name

func (*CountUnit) OnFailure

func (c *CountUnit) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*CountUnit) OnSuccess

func (c *CountUnit) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*CountUnit) Run

func (c *CountUnit) Run(ctx context.Context) error

Run executes the count unit

func (*CountUnit) SetTriggeringUnit

func (c *CountUnit) SetTriggeringUnit(unitName string)

SetTriggeringUnit sets the name of the unit that triggered this count

func (*CountUnit) Type

func (c *CountUnit) Type() string

Type returns the unit type

type CronConfig

type CronConfig struct {
	UnitConfig `yaml:",inline"`
	Schedule   string `yaml:"schedule"`
}

CronConfig represents the configuration for a cron trigger

type CronTrigger

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

CronTrigger is a trigger unit that fires based on a cron schedule

func NewCronTrigger

func NewCronTrigger(name, schedule string, state *State, onSuccess, onFailure, always []string) *CronTrigger

NewCronTrigger creates a new cron trigger unit

func (*CronTrigger) Always

func (c *CronTrigger) Always() []string

Always returns the list of units to trigger regardless of success/failure

func (*CronTrigger) Check

func (c *CronTrigger) Check(ctx context.Context) (bool, error)

Check returns true if the cron schedule has triggered since the last execution

func (*CronTrigger) Name

func (c *CronTrigger) Name() string

Name returns the name of the unit

func (*CronTrigger) OnFailure

func (c *CronTrigger) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*CronTrigger) OnSuccess

func (c *CronTrigger) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*CronTrigger) Run

func (c *CronTrigger) Run(ctx context.Context) error

Run executes the trigger unit

func (*CronTrigger) Type

func (c *CronTrigger) Type() string

Type returns the unit type

type EmailConfig

type EmailConfig struct {
	UnitConfig    `yaml:",inline"`
	To            []string `yaml:"to"`
	From          string   `yaml:"from"`
	SubjectPrefix string   `yaml:"subject_prefix,omitempty"`
	SMTPHost      string   `yaml:"smtp_host"`
	SMTPPort      int      `yaml:"smtp_port,omitempty"`
	SMTPUser      string   `yaml:"smtp_user,omitempty"`
	SMTPPassword  string   `yaml:"smtp_password,omitempty"`
	SMTPUseTLS    *bool    `yaml:"smtp_use_tls,omitempty"`
	IncludeOutput *bool    `yaml:"include_output,omitempty"`
}

EmailConfig represents the configuration for an Email unit

type EmailUnit

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

EmailUnit sends email notifications

func NewEmailUnit

func NewEmailUnit(name string, to []string, from, subjectPrefix, smtpHost string, smtpPort int,
	smtpUser, smtpPassword string, smtpUseTLS, includeOutput bool,
	onSuccess, onFailure, always []string) *EmailUnit

NewEmailUnit creates a new Email unit

func (*EmailUnit) Always

func (e *EmailUnit) Always() []string

Always returns the list of units to always trigger

func (*EmailUnit) Name

func (e *EmailUnit) Name() string

Name returns the unit name

func (*EmailUnit) OnFailure

func (e *EmailUnit) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*EmailUnit) OnSuccess

func (e *EmailUnit) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*EmailUnit) Run

func (e *EmailUnit) Run(ctx context.Context) error

Run executes the email unit

func (*EmailUnit) SetOutput

func (e *EmailUnit) SetOutput(output string)

SetOutput sets the output data from the triggering unit

func (*EmailUnit) SetTriggerError

func (e *EmailUnit) SetTriggerError(err error)

SetTriggerError sets the error from the triggering unit

func (*EmailUnit) SetTriggeringUnit

func (e *EmailUnit) SetTriggeringUnit(unitName string)

SetTriggeringUnit sets the name of the unit that triggered this email

func (*EmailUnit) Type

func (e *EmailUnit) Type() string

Type returns the unit type

type FileConfig

type FileConfig struct {
	UnitConfig `yaml:",inline"`
	Pattern    string `yaml:"pattern"`
}

FileConfig represents the configuration for a file trigger

type FileTrigger

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

FileTrigger is a trigger unit that fires when files matching a pattern change

func NewFileTrigger

func NewFileTrigger(name, pattern string, state *State, onSuccess, onFailure, always []string) *FileTrigger

NewFileTrigger creates a new file trigger unit

func (*FileTrigger) Always

func (f *FileTrigger) Always() []string

Always returns the list of units to trigger regardless of success/failure

func (*FileTrigger) Check

func (f *FileTrigger) Check(ctx context.Context) (bool, error)

Check returns true if files matching the pattern have changed

func (*FileTrigger) Name

func (f *FileTrigger) Name() string

Name returns the name of the unit

func (*FileTrigger) OnFailure

func (f *FileTrigger) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*FileTrigger) OnSuccess

func (f *FileTrigger) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*FileTrigger) Run

func (f *FileTrigger) Run(ctx context.Context) error

Run executes the trigger unit

func (*FileTrigger) Type

func (f *FileTrigger) Type() string

Type returns the unit type

type GitConfig

type GitConfig struct {
	UnitConfig `yaml:",inline"`
	Repository string `yaml:"repository"`
}

GitConfig represents the configuration for a git trigger

type GitTrigger

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

GitTrigger is a trigger unit that fires when git repository changes are detected

func NewGitTrigger

func NewGitTrigger(name, repository string, state *State, onSuccess, onFailure, always []string) *GitTrigger

NewGitTrigger creates a new git trigger unit

func (*GitTrigger) Always

func (g *GitTrigger) Always() []string

Always returns the list of units to trigger regardless of success/failure

func (*GitTrigger) Check

func (g *GitTrigger) Check(ctx context.Context) (bool, error)

Check returns true if the git repository has new commits since last check

func (*GitTrigger) Name

func (g *GitTrigger) Name() string

Name returns the name of the unit

func (*GitTrigger) OnFailure

func (g *GitTrigger) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*GitTrigger) OnSuccess

func (g *GitTrigger) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*GitTrigger) Run

func (g *GitTrigger) Run(ctx context.Context) error

Run executes the trigger unit

func (*GitTrigger) Type

func (g *GitTrigger) Type() string

Type returns the unit type

type LogConfig

type LogConfig struct {
	UnitConfig `yaml:",inline"`
	File       string `yaml:"file"`
}

LogConfig represents the configuration for a Log unit

type LogUnit

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

LogUnit writes log messages to a file

func NewLogUnit

func NewLogUnit(name, file string, onSuccess, onFailure, always []string) *LogUnit

NewLogUnit creates a new Log unit

func (*LogUnit) Always

func (l *LogUnit) Always() []string

Always returns the list of units to always trigger

func (*LogUnit) Name

func (l *LogUnit) Name() string

Name returns the unit name

func (*LogUnit) OnFailure

func (l *LogUnit) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*LogUnit) OnSuccess

func (l *LogUnit) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*LogUnit) Run

func (l *LogUnit) Run(ctx context.Context) error

Run executes the log unit

func (*LogUnit) SetOutput

func (l *LogUnit) SetOutput(output string)

SetOutput sets the output data from the triggering unit

func (*LogUnit) SetTriggeringUnit

func (l *LogUnit) SetTriggeringUnit(unitName string)

SetTriggeringUnit sets the name of the unit that triggered this log

func (*LogUnit) Type

func (l *LogUnit) Type() string

Type returns the unit type

type Orchestrator

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

Orchestrator manages unit execution and triggering

func NewOrchestrator

func NewOrchestrator(units []Unit) *Orchestrator

NewOrchestrator creates a new orchestrator with the given units

func (*Orchestrator) GetResults

func (o *Orchestrator) GetResults() map[string]*UnitResult

GetResults returns all execution results

func (*Orchestrator) Run

func (o *Orchestrator) Run(ctx context.Context) error

Run executes all units with proper trigger handling (one-time run)

func (*Orchestrator) RunDaemon

func (o *Orchestrator) RunDaemon(ctx context.Context) error

RunDaemon executes in daemon mode, continuously checking triggers

func (*Orchestrator) RunSingleUnit

func (o *Orchestrator) RunSingleUnit(ctx context.Context, unitName string, runTriggers bool) error

RunSingleUnit executes a single unit by name If runTriggers is true, the unit runs and all its triggers are executed If runTriggers is false, the unit runs in isolation without executing its triggers

type RebootConfig

type RebootConfig struct {
	UnitConfig `yaml:",inline"`
	Delay      int `yaml:"delay,omitempty"` // delay in seconds before reboot
}

RebootConfig represents the configuration for a reboot unit

type RebootUnit

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

RebootUnit is a unit that logs and reboots the system

func NewRebootUnit

func NewRebootUnit(name string, delay int, onSuccess, onFailure, always []string) *RebootUnit

NewRebootUnit creates a new reboot unit

func (*RebootUnit) Always

func (r *RebootUnit) Always() []string

Always returns the list of units to trigger regardless of success/failure

func (*RebootUnit) Name

func (r *RebootUnit) Name() string

Name returns the name of the unit

func (*RebootUnit) OnFailure

func (r *RebootUnit) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*RebootUnit) OnSuccess

func (r *RebootUnit) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*RebootUnit) Run

func (r *RebootUnit) Run(ctx context.Context) error

Run executes the reboot unit

func (*RebootUnit) Type

func (r *RebootUnit) Type() string

Type returns the unit type

type RunConfig

type RunConfig struct {
	UnitConfig `yaml:",inline"`
	Script     string `yaml:"script"`
	Directory  string `yaml:"directory,omitempty"`
	Timeout    string `yaml:"timeout,omitempty"`
}

RunConfig represents the configuration for a Run unit

type RunUnit

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

RunUnit executes shell scripts/commands

func NewRunUnit

func NewRunUnit(name, script, directory string, timeout time.Duration, onSuccess, onFailure, always []string) *RunUnit

NewRunUnit creates a new Run unit

func (*RunUnit) Always

func (r *RunUnit) Always() []string

Always returns the list of units to always trigger

func (*RunUnit) Name

func (r *RunUnit) Name() string

Name returns the unit name

func (*RunUnit) OnFailure

func (r *RunUnit) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*RunUnit) OnSuccess

func (r *RunUnit) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*RunUnit) Run

func (r *RunUnit) Run(ctx context.Context) error

Run executes the shell script

func (*RunUnit) Type

func (r *RunUnit) Type() string

Type returns the unit type

type StartConfig

type StartConfig struct {
	UnitConfig `yaml:",inline"`
}

StartConfig represents the configuration for a Start trigger

type StartTrigger

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

StartTrigger is a trigger that always fires when brun starts

func NewStartTrigger

func NewStartTrigger(name string, onSuccess, onFailure, always []string) *StartTrigger

NewStartTrigger creates a new Start trigger

func (*StartTrigger) Always

func (s *StartTrigger) Always() []string

Always returns the list of units to always trigger

func (*StartTrigger) Check

func (s *StartTrigger) Check(ctx context.Context) (bool, error)

Check always returns true since this trigger fires on every run

func (*StartTrigger) Name

func (s *StartTrigger) Name() string

Name returns the trigger name

func (*StartTrigger) OnFailure

func (s *StartTrigger) OnFailure() []string

OnFailure returns the list of units to trigger on failure

func (*StartTrigger) OnSuccess

func (s *StartTrigger) OnSuccess() []string

OnSuccess returns the list of units to trigger on success

func (*StartTrigger) Run

func (s *StartTrigger) Run(ctx context.Context) error

Run executes when the trigger fires

func (*StartTrigger) Type

func (s *StartTrigger) Type() string

Type returns the trigger type

type State

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

State represents the common state file for all units

func NewState

func NewState(filePath string) *State

NewState creates a new state manager with the given file path filePath must not be empty

func (*State) Get

func (s *State) Get(unitName, key string) (any, bool)

Get retrieves a value from state for the given unit name and key

func (*State) GetString

func (s *State) GetString(unitName, key string) (string, bool)

GetString retrieves a string value from state

func (*State) Load

func (s *State) Load() error

Load reads the state file from disk

func (*State) Save

func (s *State) Save() error

Save writes the state file to disk

func (*State) Set

func (s *State) Set(unitName, key string, value any) error

Set stores a value in state for the given unit name and key and automatically saves

func (*State) SetString

func (s *State) SetString(unitName, key, value string) error

SetString stores a string value in state and automatically saves

type TriggerUnit

type TriggerUnit interface {
	Unit

	// Check returns true if the trigger condition is met
	Check(ctx context.Context) (bool, error)

	// OnSuccess returns the names of units to trigger on success
	OnSuccess() []string

	// OnFailure returns the names of units to trigger on failure
	OnFailure() []string

	// Always returns the names of units to trigger regardless of success/failure
	Always() []string
}

TriggerUnit represents a unit that watches for conditions and triggers other units

type Unit

type Unit interface {
	// Name returns the name of the unit
	Name() string

	// Run executes the unit with the given context
	Run(ctx context.Context) error

	// Type returns the type of unit (e.g., "trigger", "task")
	Type() string
}

Unit represents a unit of work in the CI system

type UnitConfig

type UnitConfig struct {
	Name      string   `yaml:"name"`
	Type      string   `yaml:"type"`
	OnSuccess []string `yaml:"on_success,omitempty"`
	OnFailure []string `yaml:"on_failure,omitempty"`
	Always    []string `yaml:"always,omitempty"`
}

UnitConfig represents the base configuration for all units

type UnitConfigWrapper

type UnitConfigWrapper struct {
	Start  *StartConfig  `yaml:"start,omitempty"`
	Boot   *BootConfig   `yaml:"boot,omitempty"`
	Reboot *RebootConfig `yaml:"reboot,omitempty"`
	Run    *RunConfig    `yaml:"run,omitempty"`
	Log    *LogConfig    `yaml:"log,omitempty"`
	Count  *CountConfig  `yaml:"count,omitempty"`
	Cron   *CronConfig   `yaml:"cron,omitempty"`
	Email  *EmailConfig  `yaml:"email,omitempty"`
	File   *FileConfig   `yaml:"file,omitempty"`
	Git    *GitConfig    `yaml:"git,omitempty"`
}

UnitConfigWrapper wraps different unit configuration types

type UnitResult

type UnitResult struct {
	Unit   Unit
	Error  error
	Output string // Captured stdout/stderr
}

UnitResult represents the result of a unit execution

Directories

Path Synopsis
cmd
brun command

Jump to

Keyboard shortcuts

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