capistrano

package module
v0.0.0-...-6777356 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

go-ruby-capistrano/capistrano

capistrano — go-ruby-capistrano

Docs License Go Coverage

A pure-Go (no cgo) port of the core of Ruby's Capistrano — the remote-server automation / deployment framework. It reproduces Capistrano's configuration variable store (set / fetch / ask with lazily-evaluated, memoized callable values), the server / role registry and host filtering (role, server, roles(:web), release_roles, primary), the task DSL built on a Rake-style task graph (task / namespace / desc / before / after / invoke / invoke!, with circular-dependency detection), and the SSHKit-style execution context (on(hosts) { execute / test / capture / upload! / download! }) with its faithful non-zero-exit / boolean / stdout semantics — without any Ruby runtime.

It is the Capistrano backend for go-embedded-ruby (a future rbgo binding wraps this Go API as require "capistrano"), but is a standalone, reusable module — a sibling of go-ruby-rake and go-ruby-thor.

What it is — and isn't. The pure-compute half of Capistrano — the variable store and its lazy/memoized values, the server/role model and filtering, the task graph and its invoke-once / circular / before-after semantics, the deploy flow wiring, and the execute / test / capture semantics — is deterministic and needs no interpreter, so it lives here as plain Go. The effectful half — the actual SSH command execution and file transfer — is an injectable Backend: the default is a real SSH backend over golang.org/x/crypto/ssh, but tests inject an in-process FakeBackend that records commands and returns scripted output, so the whole suite is deterministic and network-free.

Capistrano is built on Rake; the standalone pure-Go Rake port is go-ruby-rake/rake. Capistrano's before / after (prepend-prerequisite / enhance-with-post-invoke) and invoke! semantics differ enough that this package carries a small purpose-built task graph rather than depending on that module.

Install

go get github.com/go-ruby-capistrano/capistrano

Usage

package main

import (
	"fmt"

	cap "github.com/go-ruby-capistrano/capistrano"
)

func main() {
	app := cap.NewApplication()

	// config (set / fetch, with a lazy memoized value)
	app.Set("application", "shop")
	app.Set("repo_url", "git@github.com:acme/shop.git")
	app.Set("deploy_to", cap.Callable(func() any { return "/srv/" + app.Fetch("application").(string) }))

	// servers & roles
	app.Role("web", []string{"web1.example.com", "web2.example.com"}, nil)
	app.AddServer("db1.example.com", []string{"db"}, map[string]any{"primary": true})

	// a custom task using the execution seam
	app.Namespace("shop", func() {
		app.Desc("Print the release path on every web host")
		app.Task("where", nil, func() error {
			return app.On(app.Roles("web"), func(s *cap.Session) error {
				out, err := s.Capture("readlink", app.Fetch("deploy_to").(string)+"/current")
				if err != nil {
					return err
				}
				fmt.Println(s.Host(), "->", out)
				return nil
			})
		})
	})

	// hooks + the built-in deploy flow
	_ = app.After("deploy:published", "shop:where")

	// In production this runs over real SSH. In a test, inject a FakeBackend:
	//   fb := cap.NewFakeBackend(); app.SetBackend(fb)
	_ = app.Invoke("deploy")
}

API (what a future rbgo binding wraps)

// Application — the DSL facade (the global `env` + the Rake application)
func NewApplication() *Application
func (app *Application) Set(key string, value any) any            // set
func (app *Application) Fetch(key string, def ...any) any         // fetch (memoizes callables)
func (app *Application) IsSet(key string) bool                    // set?
func (app *Application) Ask(key, prompt string, def any)          // ask
func (app *Application) Role(role string, hosts []string, props map[string]any)
func (app *Application) AddServer(host string, roles []string, props map[string]any) *Server
func (app *Application) Roles(names ...string) []*Server          // roles(:web)
func (app *Application) ReleaseRoles(names ...string) []*Server   // release_roles
func (app *Application) Primary(role string) *Server              // primary
func (app *Application) Desc(text string)                         // desc
func (app *Application) Task(name string, deps []string, body TaskBody) *Task
func (app *Application) Namespace(name string, body func())       // namespace
func (app *Application) Before(task, prerequisite string) error   // before
func (app *Application) After(task, post string) error            // after
func (app *Application) Invoke(name string) error                 // invoke
func (app *Application) InvokeBang(name string) error             // invoke!
func (app *Application) On(hosts []*Server, block OnBlock) error  // on(hosts) { … }
func (app *Application) Stage(name string, body func())           // stage config
func (app *Application) LoadStage(name string) error              // production / staging

// Session — the SSHKit context yielded to on-blocks
func (s *Session) Execute(command string, args ...string) error       // raises on non-zero
func (s *Session) Test(command string, args ...string) (bool, error)  // boolean
func (s *Session) Capture(command string, args ...string) (string, error) // stripped stdout
func (s *Session) Upload(local, remote string) error                  // upload!
func (s *Session) Download(remote, local string) error                // download!
func (s *Session) Host() *Server

// Backend — the injectable execution seam (default: NewSSHBackend; tests: FakeBackend)
type Backend interface {
	Run(host *Server, command string) (CommandResult, error)
	Upload(host *Server, local, remote string) error
	Download(host *Server, remote, local string) error
}
func (app *Application) SetBackend(b Backend)

// Injectable seams: SetInput (ask), SetReleaseTimestamp, SetSCM.
// Config / Server / Servers / TaskManager are exposed for direct use.

The default deploy flow (deploydeploy:startingdeploy:updatingdeploy:publishingdeploy:finishing, with deploy:check and deploy:log_revision) is registered by NewApplication, drives the Git SCM strategy, and issues every command through the injectable Backend.

Deferred (out of scope for this core)

This is a faithful core, not a drop-in production deploy tool. Explicitly deferred:

  • The plugin ecosystemcapistrano-rails, capistrano-bundler, capistrano-rbenv, rvm/passenger/puma/sidekiq tasks, etc. The task DSL and hooks are all present, so such plugins are ordinary task definitions layered on top.
  • Real file transfer over the default SSH backendupload! / download! return a "deferred" error on the built-in SSH backend (they need an SFTP/SCP sub-channel). The Backend seam is complete, so a host injects a transferring backend; the FakeBackend records transfers for tests.
  • SSH connection multiplexing / parallel onon(hosts) runs the block sequentially per host. SSHKit's in: :parallel/:groups, connection pooling, and ControlMaster reuse are not implemented.
  • Full mirror-clone / archive release strategy & symlinked shared pathsdeploy:updating performs a compact git clone rather than Capistrano's cached-mirror + git archive + linked_files / linked_dirs machinery.
  • ask echo/redaction options, run_locally, Rake-style file tasks and rules, the CLI (cap), and Airbrussh formatting.

Tests & coverage

The suite is Ruby-free and network-free. It drives the full DSL and a sample deploy flow against the in-process FakeBackend, and covers the real SSH backend with an in-process loopback ssh server (over x/crypto/ssh), so no external host is needed. Coverage is held at 100% of statements, including every error branch (command non-zero exit, missing task, no matching hosts, capture of a failing command, transport failure).

go test -covermode=count -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

CGO-free, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x), the two WebAssembly targets (js/wasm, wasip1/wasm), and three OSes (Linux, macOS, Windows).

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-capistrano/capistrano authors.

Documentation

Overview

Package capistrano is a pure-Go (no cgo) port of the core of Ruby's Capistrano — the remote-server automation / deployment framework. It reproduces Capistrano's configuration variable store (set / fetch / ask with lazily-evaluated, memoized callable values), the server / role registry and host filtering (role, server, roles(:web), release_roles, primary), the task DSL built on a Rake-style task graph (task / namespace / desc / before / after / invoke / invoke!, with circular-dependency detection), and the SSHKit-style execution context (on(hosts) { execute / test / capture / upload! / download! }) with its faithful nonzero-exit / boolean / stdout semantics.

What it is — and isn't. The *pure-compute* half of Capistrano — the variable store and its lazy/memoized values, the server/role model and filtering, the task graph and its invoke-once / circular / before-after semantics, the deploy flow wiring, and the execute/test/capture semantics — is deterministic and needs no interpreter, so it lives here as plain Go. The *effectful* half — the actual SSH command execution and file transfer — is an injectable Backend interface: the default is a real SSH backend over golang.org/x/crypto/ssh, but tests inject an in-process FakeBackend that records commands and returns scripted output, so the whole suite is deterministic and network-free. A future rbgo binding wraps this Go API as `require "capistrano"`; it does not import rbgo.

The task graph mirrors Rake (Capistrano is built on Rake); the standalone pure-Go Rake port is github.com/go-ruby-rake/rake. Capistrano's before/after (prepend-prerequisite / enhance-with-post-invoke) and invoke! semantics differ enough that this package carries a small purpose-built graph rather than depending on that module.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Application

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

Application is the Capistrano DSL facade — the port of the global `env` (Capistrano::Configuration.env) plus the Rake application. It aggregates the variable store, the server/role registry, the task graph, the injectable command Backend, the interactive-input seam (for ask), and the registered stages, and exposes the Ruby-named DSL methods (set/fetch/ask, role/server/ roles/…, task/namespace/before/after/invoke, on) a host binds.

func NewApplication

func NewApplication() *Application

NewApplication returns a fully-wired DSL facade: an empty variable store, server registry and task graph, the default real-SSH Backend (host-key checks disabled, no auth — a deploy overrides it via set / a custom ssh.ClientConfig), a non-interactive input seam (ask falls back to its default), a fixed release timestamp, and the Git SCM strategy. The default deploy flow is registered.

func (*Application) AddServer

func (app *Application) AddServer(host string, roles []string, props map[string]any) *Server

AddServer registers a server with roles and properties (the `server` DSL).

func (*Application) After

func (app *Application) After(task, post string) error

After wires post to run after task (the `after` DSL).

func (*Application) Ask

func (app *Application) Ask(key, prompt string, def any)

Ask registers key as a question (the `ask` DSL): on first fetch it prompts via the input seam, falling back to def when the answer is blank, and memoizes.

func (*Application) Backend

func (app *Application) Backend() Backend

Backend returns the installed command Backend.

func (*Application) Before

func (app *Application) Before(task, prerequisite string) error

Before wires prerequisite to run before task (the `before` DSL).

func (*Application) Config

func (app *Application) Config() *Configuration

Config exposes the underlying variable store.

func (*Application) Desc

func (app *Application) Desc(text string)

Desc sets the next task's description (the `desc` DSL).

func (*Application) Fetch

func (app *Application) Fetch(key string, def ...any) any

Fetch resolves key with an optional default (the `fetch` DSL), memoizing any callable it evaluates.

func (*Application) Invoke

func (app *Application) Invoke(name string) error

Invoke runs a task once (the `invoke` DSL).

func (*Application) InvokeBang

func (app *Application) InvokeBang(name string) error

InvokeBang re-runs a task even if already invoked (the `invoke!` DSL).

func (*Application) IsSet

func (app *Application) IsSet(key string) bool

IsSet reports whether key is assigned (the `set?` DSL).

func (*Application) LoadStage

func (app *Application) LoadStage(name string) error

LoadStage runs the named stage's body, defining its servers, roles and variables. It raises a Capistrano error when no such stage is registered.

func (*Application) Namespace

func (app *Application) Namespace(name string, body func())

Namespace scopes body's task definitions under name (the `namespace` DSL).

func (*Application) On

func (app *Application) On(hosts []*Server, block OnBlock) error

On runs block once per host with a Session bound to it (the `on(hosts) { … }` DSL). With no hosts it raises NoMatchingServersError; the first block error aborts and is returned.

func (*Application) Primary

func (app *Application) Primary(role string) *Server

Primary returns the primary server for role (the `primary` DSL).

func (*Application) ReleaseRoles

func (app *Application) ReleaseRoles(names ...string) []*Server

ReleaseRoles returns the release-eligible servers in the roles (the `release_roles` DSL).

func (*Application) Role

func (app *Application) Role(role string, hosts []string, props map[string]any)

Role assigns role to hosts (the `role name, hosts` DSL).

func (*Application) Roles

func (app *Application) Roles(names ...string) []*Server

Roles returns the servers in any of the named roles (the `roles` DSL).

func (*Application) Servers

func (app *Application) Servers() *Servers

Servers exposes the underlying server/role registry.

func (*Application) Set

func (app *Application) Set(key string, value any) any

Set binds key (the `set` DSL); value may be a Callable for a lazy variable.

func (*Application) SetBackend

func (app *Application) SetBackend(b Backend)

SetBackend injects a Backend (a FakeBackend in tests, a custom transferring backend in production) — the seam the whole execution layer hangs on.

func (*Application) SetInput

func (app *Application) SetInput(fn func(prompt string) string)

SetInput injects the interactive-input seam ask reads answers from (rbgo wires it to $stdin; tests wire a scripted function).

func (*Application) SetReleaseTimestamp

func (app *Application) SetReleaseTimestamp(fn func() string)

SetReleaseTimestamp injects the release-directory naming seam.

func (*Application) SetSCM

func (app *Application) SetSCM(s SCM)

SetSCM injects the source-control strategy the deploy flow uses.

func (*Application) Stage

func (app *Application) Stage(name string, body func())

Stage registers a named stage body (a production/staging config block); Load runs it (the `capistrano/setup` stage mechanism).

func (*Application) Task

func (app *Application) Task(name string, deps []string, body TaskBody) *Task

Task defines/enhances a task (the `task` DSL).

func (*Application) Tasks

func (app *Application) Tasks() *TaskManager

Tasks exposes the underlying task graph.

type Backend

type Backend interface {
	Run(host *Server, command string) (CommandResult, error)
	Upload(host *Server, local, remote string) error
	Download(host *Server, remote, local string) error
}

Backend is the injectable command-execution seam — the SSHKit backend behind Capistrano's on(hosts) { … } context. Run executes one command on one host and reports the CommandResult; a returned error means the command could not be run at all (dial/transport failure), distinct from a non-zero exit. Upload and Download transfer a file to/from the host. The default is a real SSH backend (see NewSSHBackend); tests inject a FakeBackend.

func NewSSHBackend

func NewSSHBackend(config *ssh.ClientConfig) Backend

NewSSHBackend returns the default real-SSH Backend using config to authenticate. It is the Backend NewApplication installs; a deploy that never injects a different Backend uses this one to reach real hosts over golang.org/x/crypto/ssh.

type Callable

type Callable func() any

Callable is a lazily-evaluated configuration value — the port of a Proc stored in the Capistrano variable table. When fetch encounters one it calls it and memoizes the result back into the table (Configuration#fetch's `while callable? … set(key, value.call)` loop), so the body runs at most once.

type CapistranoError

type CapistranoError interface {
	error
	// contains filtered or unexported methods
}

CapistranoError is the root of the Capistrano error tree (Capistrano::Error < StandardError). Every error this package raises satisfies it, so a host (rbgo) can map the whole family onto a single Ruby exception class and still switch on the concrete Go type for the specific subclass.

type CommandError

type CommandError struct {
	Host       string
	Command    string
	ExitStatus int
	Stderr     string
}

CommandError is the port of SSHKit::Command::Failed — raised when execute / capture run a command that exits non-zero. It carries the host, the command line, the exit status, and the captured stderr so the message reproduces SSHKit's multi-line report.

func (*CommandError) Error

func (e *CommandError) Error() string

type CommandResult

type CommandResult struct {
	Stdout     string
	Stderr     string
	ExitStatus int
}

CommandResult is the outcome of running one shell command on one host — the pure-data slice of an SSHKit::Command after it has run. ExitStatus is the process exit code (0 on success); Stdout / Stderr are the captured streams. A non-zero ExitStatus is NOT a transport error — the Backend reports it here and the Session decides whether to raise (execute / capture do; test does not).

type Configuration

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

Configuration is Capistrano's variable store — the port of the variable half of Capistrano::Configuration. It maps symbols (as strings) to values, where a value may be a plain any or a Callable that is resolved and memoized on first fetch.

func NewConfiguration

func NewConfiguration() *Configuration

NewConfiguration returns an empty variable store.

func (*Configuration) Delete

func (c *Configuration) Delete(key string) any

Delete removes key, returning its previous value (Configuration#delete).

func (*Configuration) Fetch

func (c *Configuration) Fetch(key string, def ...any) any

Fetch resolves key (Capistrano's `fetch`). When key is unset the optional default is used (a Callable default is honoured, as MRI's block default is). Any Callable encountered — stored value or default — is invoked and its result memoized back under key, so the body runs at most once and later fetches are cheap. A missing key with no default yields nil.

func (*Configuration) IsSet

func (c *Configuration) IsSet(key string) bool

IsSet reports whether key has been assigned (Configuration#key? / `set?`).

func (*Configuration) Keys

func (c *Configuration) Keys() []string

Keys returns every assigned variable name, sorted (Configuration#keys).

func (*Configuration) Set

func (c *Configuration) Set(key string, value any) any

Set binds key to value (Capistrano's `set`). value may be a Callable for a lazily-evaluated variable. Set returns value, mirroring MRI's `set`.

type Dialer

type Dialer interface {
	Dial(host *Server) (SSHConn, error)
}

Dialer opens an SSHConn to a host — the injectable seam under the SSH backend. The default (sshDialer) dials over the network with x/crypto/ssh; tests inject a fake, and an in-process ssh server exercises the default.

type Error

type Error struct{ Message string }

Error is a plain Capistrano::Error carrying a message — the base node of the tree, raised where MRI would raise Capistrano::Error itself (e.g. an unknown stage, a circular task dependency).

func (*Error) Error

func (e *Error) Error() string

type FakeBackend

type FakeBackend struct {
	// Commands is the ordered log of every command Run received.
	Commands []string
	// Uploads / Downloads log every transfer as "local -> remote".
	Uploads   []string
	Downloads []string
	// contains filtered or unexported fields
}

FakeBackend is an in-process, deterministic Backend for tests — the port of SSHKit's test backend. It records every command / upload / download and returns scripted CommandResults keyed by the command string (falling back to a zero-exit empty result, so a plain command "succeeds" unless scripted otherwise). It performs no I/O and needs no network, so the suite is reproducible on every architecture.

func NewFakeBackend

func NewFakeBackend() *FakeBackend

NewFakeBackend returns an empty fake backend.

func (*FakeBackend) Download

func (f *FakeBackend) Download(host *Server, remote, local string) error

Download records the transfer and honours a scripted download failure.

func (*FakeBackend) FailDownloads

func (f *FakeBackend) FailDownloads(err error) *FakeBackend

func (*FakeBackend) FailTransport

func (f *FakeBackend) FailTransport(err error) *FakeBackend

FailTransport makes every Run report err as a transport failure (dial down).

func (*FakeBackend) FailUploads

func (f *FakeBackend) FailUploads(err error) *FakeBackend

FailUploads / FailDownloads make every transfer of that kind fail.

func (*FakeBackend) Run

func (f *FakeBackend) Run(host *Server, command string) (CommandResult, error)

Run records command and returns its scripted result (or a zero-exit empty result when unscripted), honouring a global transport failure.

func (*FakeBackend) Script

func (f *FakeBackend) Script(command string, res CommandResult, err error) *FakeBackend

Script makes the fake return res (and err as the transport error) whenever the exact command line is run. It returns the backend for chaining.

func (*FakeBackend) Upload

func (f *FakeBackend) Upload(host *Server, local, remote string) error

Upload records the transfer and honours a scripted upload failure.

type Git

type Git struct{}

Git is the default SCM strategy — the port of Capistrano::SCM::Git. It issues git over the Session; it does not implement git itself.

func (Git) Check

func (Git) Check(s *Session, c *Configuration) error

Check runs `git ls-remote <repo_url>` on the host, raising if the repository is unreachable (deploy:check's git access probe).

func (Git) Release

func (Git) Release(s *Session, c *Configuration) error

Release clones the branch into release_path (deploy:updating). It first ensures the release directory exists, then clones — a compact stand-in for Capistrano's mirror-clone + archive strategy (see the README "Deferred" note).

func (Git) Revision

func (Git) Revision(s *Session, c *Configuration) (string, error)

Revision captures the deployed commit SHA (`git rev-parse HEAD` in the release), used for the revisions.log entry.

type NoMatchingServersError

type NoMatchingServersError struct{ Filter string }

NoMatchingServersError is Capistrano::NoMatchingServersError — raised when an action needs at least one server but the role/property filter selected none.

func (*NoMatchingServersError) Error

func (e *NoMatchingServersError) Error() string

type OnBlock

type OnBlock func(s *Session) error

OnBlock is the body of an on(hosts) { … } context, run once per matching host with a Session bound to that host — the port of the SSHKit block. A returned error aborts the run and is propagated to the caller.

type SCM

type SCM interface {
	// Check verifies the deploy host can reach the repository (deploy:check).
	Check(s *Session, c *Configuration) error
	// Release materialises a release into c's release_path (deploy:updating).
	Release(s *Session, c *Configuration) error
	// Revision captures the deployed revision SHA (deploy:log_revision).
	Revision(s *Session, c *Configuration) (string, error)
}

SCM is the source-control strategy the deploy flow drives through the execution seam — the port of Capistrano's SCM plugins (Capistrano::SCM::Git and friends). Every method issues its commands through the given Session, so the actual git invocations flow through the injectable Backend like any other command.

type SSHConn

type SSHConn interface {
	// Run executes command over the connection and reports the CommandResult. A
	// non-zero exit is returned in the result (ExitStatus), not as an error; a
	// returned error is a transport/protocol failure.
	Run(command string) (CommandResult, error)
	// Close tears the connection down.
	Close() error
}

SSHConn is one live SSH connection to a host — the small surface the SSH backend needs from golang.org/x/crypto/ssh. It is an interface so the backend orchestration is testable with an in-process fake, while the real implementation (sshConn) wraps *ssh.Client.

type Server

type Server struct {
	User string
	Host string
	Port int
	// contains filtered or unexported fields
}

Server is a single deployment target — the port of Capistrano::Configuration:: Server (an SSHKit::Host). It carries the connection triple (user, host, port), the set of roles it plays, and an arbitrary property bag (Capistrano's per-server variables such as primary, no_release, ssh_options).

func NewServer

func NewServer(hostString string) *Server

NewServer parses an SSHKit host string ("[user@]host[:port]") into a Server — the port of Server.new / SSHKit::Host#initialize. A missing user or port is left zero-valued (the host's defaults apply).

func (*Server) AddRole

func (s *Server) AddRole(roles ...string) *Server

AddRole records that the server plays each of roles (Server#add_role), returning the server for chaining. The special role :all is never stored — it is a wildcard the registry expands.

func (*Server) Fetch

func (s *Server) Fetch(key string) any

Fetch reads a per-server property, or nil when unset (Server#fetch).

func (*Server) HasProperty

func (s *Server) HasProperty(key string) bool

HasProperty reports whether a per-server property has been set.

func (*Server) HasRole

func (s *Server) HasRole(role string) bool

HasRole reports whether the server plays role (Server#roles.include?). The wildcard :all matches every server.

func (*Server) HostPort

func (s *Server) HostPort() string

HostPort returns the "host:port" dial target, defaulting the port to 22 when unset (the SSH default).

func (*Server) IsPrimary

func (s *Server) IsPrimary() bool

IsPrimary reports whether the server was flagged `primary: true` (Server# primary?). The registry prefers such a server when resolving primary(role).

func (*Server) NoRelease

func (s *Server) NoRelease() bool

NoRelease reports whether the server opted out of releases via `no_release: true` (Server#no_release?), excluding it from release_roles.

func (*Server) Roles

func (s *Server) Roles() []string

Roles returns the server's roles, sorted (Server#roles as a stable slice).

func (*Server) Set

func (s *Server) Set(key string, value any) *Server

Set assigns a per-server property (Server#set / with), returning the server.

func (*Server) String

func (s *Server) String() string

String renders the canonical "[user@]host[:port]" form (Server#to_s).

type Servers

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

Servers is the server/role registry — the port of Capistrano::Configuration:: Servers. It keeps an ordered, de-duplicated list of Server targets (SSHKit merges re-declarations of the same endpoint) and answers the role/property filter queries the DSL exposes: roles(:web), release_roles, primary(role).

func NewServers

func NewServers() *Servers

NewServers returns an empty registry.

func (*Servers) Add

func (s *Servers) Add(host string, roles []string, props map[string]any) *Server

Add registers a server with the given roles and properties (the DSL's `server host, roles: [...], **props`), merging into any existing endpoint.

func (*Servers) AddRole

func (s *Servers) AddRole(role string, hosts []string, props map[string]any)

AddRole assigns role to every host, with shared properties (the DSL's `role name, hosts, **props`).

func (*Servers) All

func (s *Servers) All() []*Server

All returns every registered server in declaration order (roles(:all)).

func (*Servers) Primary

func (s *Servers) Primary(role string) *Server

Primary returns the primary server for role (the DSL's primary(role)): the first host flagged `primary: true`, else the first host in the role, else nil.

func (*Servers) ReleaseRoles

func (s *Servers) ReleaseRoles(names ...string) []*Server

ReleaseRoles is Roles minus any server flagged no_release (the DSL's release_roles) — the hosts that actually receive a release.

func (*Servers) RoleNames

func (s *Servers) RoleNames() []string

RoleNames returns every distinct role across the registry, sorted (the DSL's role_names).

func (*Servers) Roles

func (s *Servers) Roles(names ...string) []*Server

Roles returns the servers playing any of the named roles, in declaration order (the DSL's roles(:web, :app)). The wildcard :all selects every server.

type Session

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

Session is the per-host execution context yielded to an on-block — the port of SSHKit's backend receiver. Its execute / test / capture reproduce SSHKit's semantics faithfully: execute raises on a non-zero exit, test returns a boolean, capture returns stripped stdout (and raises on a non-zero exit).

func (*Session) Capture

func (s *Session) Capture(command string, args ...string) (string, error)

Capture runs command and returns its stripped stdout — the port of SSHKit's `capture`. Like execute it raises a CommandError on a non-zero exit.

func (*Session) Download

func (s *Session) Download(remote, local string) error

Download transfers remote to local from the host, raising on failure — the port of `download!`.

func (*Session) Execute

func (s *Session) Execute(command string, args ...string) error

Execute runs command and raises a CommandError if it exits non-zero — the port of SSHKit's `execute` (and Capistrano's `execute`/`execute!`). It returns nil on success and the transport error if the command could not be run.

func (*Session) Host

func (s *Session) Host() *Server

Host returns the server this session targets (the block's `host`).

func (*Session) Test

func (s *Session) Test(command string, args ...string) (bool, error)

Test runs command and reports whether it exited zero — the port of SSHKit's `test` (a boolean predicate that never raises on a non-zero exit). A transport failure is still returned as an error.

func (*Session) Upload

func (s *Session) Upload(local, remote string) error

Upload transfers local to remote on the host, raising on failure — the port of `upload!`.

type Task

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

Task is one unit of work in the graph — the Capistrano/Rake Task. It carries a fully-qualified (namespace-prefixed) name, its ordinary prerequisites, its after-hooks (invoked once its own actions succeed), the action bodies, a description, and the invoke-once guard with its remembered error.

func (*Task) Description

func (t *Task) Description() string

Description returns the task's desc text (Rake::Task#comment).

func (*Task) Name

func (t *Task) Name() string

Name returns the task's fully-qualified name.

func (*Task) Prerequisites

func (t *Task) Prerequisites() []string

Prerequisites returns the ordinary prerequisite names, in invoke order.

func (*Task) Reenable

func (t *Task) Reenable()

Reenable clears the invoke-once guard so the task runs again (Task#reenable, the mechanism behind invoke!).

type TaskBody

type TaskBody func() error

TaskBody is a task's action body — the seam for a Capistrano `task … do … end` block. A host (rbgo) wires each body to the Ruby block; tests pass Go closures. Returning an error aborts the invocation (an exception escaping the block), and the error is remembered so a later invoke re-raises it.

type TaskManager

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

TaskManager is the task registry and namespace resolver — the Rake half of Capistrano's DSL. It owns the task table (keyed by fully-qualified name), the live namespace stack, and the pending description, and drives invoke with its depth-first, prerequisite-first, invoke-once walk plus circular detection.

func NewTaskManager

func NewTaskManager() *TaskManager

NewTaskManager returns an empty registry at the top-level scope.

func (*TaskManager) After

func (m *TaskManager) After(task, post string) error

After makes post run after task's own actions — the `after` DSL. Capistrano enhances task to invoke post once its body has run.

func (*TaskManager) Before

func (m *TaskManager) Before(task, prerequisite string) error

Before makes prerequisite run before task — the `before` DSL. Capistrano prepends it to task's prerequisites, so the most-recently-declared before-hook runs first. Both names are resolved against the current scope.

func (*TaskManager) Define

func (m *TaskManager) Define(name string, deps []string, body TaskBody) *Task

Define registers (or, when it already exists, enhances) a task named within the current namespace, with the given prerequisites and body — the `task` DSL. Re-defining a task appends its prerequisites (union) and its body, matching Rake's define_task enhancement.

func (*TaskManager) Desc

func (m *TaskManager) Desc(text string)

Desc records the description applied to the next defined task (the `desc` DSL); it is consumed by the following Define, then cleared.

func (*TaskManager) Invoke

func (m *TaskManager) Invoke(name string) error

Invoke runs the task, prerequisites first, honouring the invoke-once guard — the `invoke` DSL. A task already invoked is skipped (its remembered error, if any, is re-raised).

func (*TaskManager) InvokeBang

func (m *TaskManager) InvokeBang(name string) error

InvokeBang re-enables the task (only) and invokes it, forcing it to run again even if already invoked — the `invoke!` DSL (Rake's invoke!).

func (*TaskManager) Lookup

func (m *TaskManager) Lookup(name string) (*Task, bool)

Lookup resolves a task by fully-qualified name (Rake::Task[]). It returns the task and whether it was found.

func (*TaskManager) Namespace

func (m *TaskManager) Namespace(name string, body func())

Namespace evaluates body with name pushed on the scope stack (the `namespace` DSL), so tasks defined inside are prefixed "name:". Namespaces nest.

func (*TaskManager) Tasks

func (m *TaskManager) Tasks() []*Task

Tasks returns every registered task, sorted by name.

type TaskNotFoundError

type TaskNotFoundError struct{ Name string }

TaskNotFoundError is raised when invoke targets a name no task is registered under — the port of the "Don't know how to build task" failure Rake raises through Capistrano's DSL.

func (*TaskNotFoundError) Error

func (e *TaskNotFoundError) Error() string

Jump to

Keyboard shortcuts

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