Skip to content

Latest commit

 

History

History
211 lines (159 loc) · 4.68 KB

File metadata and controls

211 lines (159 loc) · 4.68 KB

Runtime Resource Model

Core idea

A resource represents one desired piece of machine state.

Examples:

  • brew:git
  • brew_tap:vwall/kitout
  • asdf_plugin:ruby
  • asdf_tool_versions:~/.tool-versions
  • cask:ghostty
  • directory:~/code
  • copy:~/.codex/skills/nuxt-practices
  • symlink:~/.zshrc
  • repo:~/code/example-project
  • macos_default:NSGlobalDomain/AppleShowAllExtensions

Every resource can be checked. Some resources can be applied.

Resource.ID() and Resource.Type() are the canonical runtime identity. The planner and executor stamp that identity onto plan and apply items rather than trusting duplicated identity fields returned by resource implementations.

Interface

type Resource interface {
    ID() string
    Type() string
    Status(ctx context.Context) (StatusResult, error)
    Apply(ctx context.Context) (ApplyResult, error)
}

StatusResult

type ResourceState string

const (
    StateSatisfied ResourceState = "satisfied"
    StateMissing   ResourceState = "missing"
    StateChanged   ResourceState = "changed"
    StateFailed    ResourceState = "failed"
    StateSkipped   ResourceState = "skipped"
    StateUnknown   ResourceState = "unknown"
)

type StatusResult struct {
    ResourceID string
    Type       string
    State      ResourceState
    Message    string
    Details    map[string]string
}

ApplyResult

type ApplyResult struct {
    ResourceID string
    Type       string
    Action     string
    Changed    bool
    Message    string
    Details    map[string]string
}

Required properties

Idempotent

Running apply twice should not apply the same change twice.

Observable

Status should be able to explain what it found.

Safe

Status and dry-run must never modify the system.

Local

Resources should operate on the local machine only in the MVP.

Resource IDs

IDs should be stable and human-readable.

Examples:

brew:git
brew_tap:vwall/kitout
asdf_plugin:ruby
asdf_tool_versions:/Users/example/.tool-versions
cask:visual-studio-code
directory:/Users/example/code
copy:/Users/example/.codex/skills/nuxt-practices
symlink:/Users/example/.zshrc
repo:/Users/example/code/example-project
macos_default:NSGlobalDomain/AppleShowAllExtensions

Planner

The planner turns status results into actions.

satisfied -> no-op
missing -> apply
changed -> apply if safe, otherwise warn
failed -> fail
skipped -> skip
unknown -> fail unless explicitly allowed

Planning and execution reports carry a report-level ExecutionError. This preserves cancellation even when a config produces no resource items; per-item failures remain reserved for resource-specific problems.

Executor

The executor runs actions in order.

MVP execution should be sequential. Parallel execution can come later.

Sequential execution makes output easier to follow and avoids command conflicts, especially with Homebrew.

Dependency handling

Do not build a full dependency graph in the MVP.

Use fixed execution order:

  1. doctor prerequisites
  2. FileVault requirement
  3. system prerequisites
  4. Homebrew taps
  5. Homebrew packages
  6. asdf plugins and versions
  7. asdf .tool-versions entries
  8. casks
  9. directories
  10. repositories
  11. copies
  12. symlinks
  13. macOS defaults
  14. firewall security settings
  15. SSH keys
  16. login shell
  17. shell commands

This is enough for a first version.

Dry-run behavior

Dry-run should:

  • load config
  • validate config
  • check statuses
  • build a plan
  • render intended changes
  • make no changes

Dry-run should not:

  • add Homebrew taps
  • install packages
  • clone repositories
  • create directories
  • copy files
  • create symlinks
  • update security settings
  • install system prerequisites
  • generate SSH keys
  • run shell commands
  • write files

Command output capture

Commands whose output is only diagnostic use platform.WithBoundedOutput at the individual mutation call. The executable runner retains at most 64 KiB from the end of each stdout/stderr stream and marks truncated captures in errors. Verbose streaming still forwards complete output to the configured writers.

The default runner preserves complete output for parsers. Keep inventory/status reads, ssh-keygen -y (public-key data), and asdf installs (full-log failure guidance) on this path. Do not apply bounded capture to an entire resource runner, because its status and apply methods may share that runner.

Exit codes

Recommended exit codes:

0 all satisfied or apply completed successfully
1 status found resources needing attention
2 validation error
3 runtime error
4 partial apply failure

Cancellation is a runtime error (3). Resource apply failures remain 4, even when some earlier resources were applied successfully.