storm

package module
v0.2.8 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 25 Imported by: 0

README

Storm

Storm is an automation agent that helps to run workflows on remote or local machines.

Installation

For use in golang

go get github.com/overal-x/storm

For Linux and MacOS

curl -fsSL https://raw.githubusercontent.com/overal-x/formatio.storm/main/scripts/install.sh | bash

For Windows

irm https://raw.githubusercontent.com/overal-x/formatio.storm/main/scripts/install.ps1 | iex

Or download binaries from release page

GitHub Action

Use the reusable setup action from another repository workflow:

jobs:
	build:
		runs-on: ubuntu-latest
		steps:
			- uses: actions/checkout@v4
			- name: Setup Storm
				uses: overal-x/storm/actions/setup-storm@v0.1.1
			- name: Check Storm
				run: storm version

Compatibility note:

  • The legacy subpath also works: overal-x/storm/.github/actions/setup-storm@v0.1.1
  • Prefer pinning to a tag or commit SHA instead of @main in production workflows.

Single-file deploy action (setup SSH + setup Storm + deploy):

jobs:
	deploy:
		runs-on: ubuntu-latest
		steps:
			- uses: actions/checkout@v4
			- name: Storm deploy
				uses: overal-x/storm/actions/storm-deploy@v0.1.1
				with:
					ssh_key: ${{ secrets.SSH_KEY_B64 }}
					github_token: ${{ secrets.GITHUB_TOKEN }}
					github_repo: ${{ github.repository }}
					github_ref: ${{ github.ref_name }}
					inventory: .storm/inventory.yaml
					workflow: .storm/workflow.yaml

Usage

With the example files

Run against remote machines from inventory

storm agent install -i ./samples/basic/inventory.yaml
storm agent run -i ./samples/basic/inventory.yaml ./samples/basic/workflow.yaml

Run worklow on current host

storm run ./samples/basic/workflow.yaml

Contexts

Workflows can reference context values with ${{ <name>.<key> }} expressions (for example ${{ github.REPO_NAME }}). Contexts are supplied at run time and work identically for both storm run and storm agent run.

Inline — repeatable --context/-c flag in name:value form, where the value is JSON (or base64-encoded JSON with --context-format base64):

storm run -c 'github:{"REPO_NAME":"formatio.storm"}' ./samples/workflow-context.yaml

From a file — repeatable --context-file flag. Files may be YAML or JSON, in one of two forms:

  • Whole-contexts file — the file's top-level keys are the context names:

    storm run --context-file ./samples/context.yaml ./samples/workflow-context.yaml
    
  • Named single-context file — the file holds one context's key/value map, assigned under <name>:

    storm run --context-file github:./gh.json ./samples/workflow-context.yaml
    

Precedence--context-file provides the base; inline --context flags override individual keys. Both flags may be repeated and combined.

Defaults

A workflow-level defaults block sets fallbacks applied to every job step, so you don't have to repeat shell or directory on each step:

name: My workflow

defaults:
  directory: ./app
  shell: /bin/bash

jobs:
  - name: build
    runs-on: self-hosted
    steps:
      - name: install # runs in ./app with /bin/bash
        run: npm ci
      - name: test # overrides just the directory
        directory: ./app/tests
        run: npm test

Precedence for each step, highest first: the step's own directory/shell, then defaults, then the workflow-level directory (for directory) or $SHELL (for shell).

Development

git clone git@github.com:overal-x/formatio.storm.git
go mod tidy
go run ./cmd help

Documentation

Index

Constants

View Source
const (
	ContextFormatJSON   = "json"
	ContextFormatBase64 = "base64"
)
View Source
const (
	StepOutputTypePlain = iota + 1
	StepOutputTypeStruct
	StepOutputTypeJson
)

Variables

This section is empty.

Functions

func BuildContexts

func BuildContexts(files, flags []string, format string) (map[string]map[string]any, error)

BuildContexts combines context files and inline context flags into a single context map. Files are applied first (providing the base), then inline flags override individual keys.

func ChdirOrCreate

func ChdirOrCreate(dir string) error

func LoadContextFiles

func LoadContextFiles(files []string) (map[string]map[string]any, error)

LoadContextFiles reads context from one or more YAML/JSON files into the nested context map. Because YAML is a superset of JSON, a single YAML parser handles both formats.

Each entry may take one of two forms:

  • "path": the file's top-level keys are context names, each mapping to that context's key/value map (a whole-contexts file).
  • "name:path": the file holds a single context's key/value map, assigned under "name".

The forms are disambiguated by splitting on the first ":" — if the left part is a bare word (\w+) it is treated as "name:path"; otherwise the whole entry is treated as a "path". Files are merged in order, with later files overriding earlier ones at the key level.

func MergeContexts

func MergeContexts(dst, src map[string]map[string]any)

MergeContexts merges src into dst at the key level: for each context name in src, keys are copied into the matching context map in dst (created if absent), with src values overriding existing ones.

func ParseContextFlags

func ParseContextFlags(flags []string, format string) (map[string]map[string]any, error)

ParseContextFlags parses CLI --context flags in "name:value" format into a nested map. Each flag is split on the first ":" only, so JSON values containing colons are handled correctly.

The format parameter controls how the value portion is interpreted:

  • "json" (default): value is raw JSON
  • "base64": value is a base64-encoded JSON string

func RenderTemplate

func RenderTemplate(content string, contexts map[string]map[string]any) (string, error)

RenderTemplate resolves all ${{ context.key }} expressions in content using the provided context maps. Rendering is in-memory only.

func ShellEscape

func ShellEscape(cmd string) string

Types

type Agent

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

func NewAgent

func NewAgent() *Agent

func (*Agent) AgentWithCallback

func (a *Agent) AgentWithCallback(callback func(interface{}), format int) RunOption

func (*Agent) AgentWithConfigs

func (a *Agent) AgentWithConfigs(w WorkflowConfig, i InventoryConfig) RunOption

func (*Agent) AgentWithContexts

func (a *Agent) AgentWithContexts(contexts map[string]map[string]any) RunOption

func (*Agent) AgentWithFiles

func (a *Agent) AgentWithFiles(w string, i string) RunOption

func (*Agent) Install

func (a *Agent) Install(args InstallArgs) error

func (*Agent) InstallDev

func (a *Agent) InstallDev(ic InventoryConfig) error

This is meant for testing locally or in CI

func (*Agent) InstallProd

func (a *Agent) InstallProd(ic InventoryConfig) error

func (*Agent) Run

func (a *Agent) Run(opts ...RunOption) error

func (*Agent) Uninstall

func (a *Agent) Uninstall(args UninstallArgs) error

type AuthenticateArgs

type AuthenticateArgs struct {
	User          string
	Password      string
	Host          string
	Port          int
	PrivateSshKey string
}

type CopyFileArgs

type CopyFileArgs struct {
	Client                *ssh.Client
	From, To, Permissions string
}

type Defaults

type Defaults struct {
	// Directory to run steps from, overrides the workflow-level Directory
	// and is itself overridden by a step's own Directory.
	Directory string `yaml:"directory,omitempty"`

	// Shell to run steps with, overridden by a step's own Shell.
	Shell string `yaml:"shell,omitempty"`
}

type ExecuteArgs

type ExecuteArgs struct {
	Directory      string
	Shell          string
	Command        string
	OutputCallback func(string)
	ErrorCallback  func(string)
}

type ExecuteCommandArgs

type ExecuteCommandArgs struct {
	Client         *ssh.Client
	Command        string
	OutputCallback func(string)
	ErrorCallback  func(string)
}

type InstallArgs

type InstallArgs struct {
	If string
	Ic InventoryConfig

	// Installation mode; options are `dev` or `prod`
	Mode string
}

type Inventory

type Inventory struct{}

func NewInventory

func NewInventory() *Inventory

func (*Inventory) Decrypt

func (i *Inventory) Decrypt(ciphertext string, decryptionKey string) (*string, error)

func (*Inventory) Encrypt

func (i *Inventory) Encrypt(file string, encryptionKey string) (*string, error)

func (*Inventory) Load

func (i *Inventory) Load(file string) (*InventoryConfig, error)

type InventoryConfig

type InventoryConfig struct {
	Servers []Server `yaml:"servers"`
}

type Job

type Job struct {
	Name   string `yaml:"name"`
	RunsOn string `yaml:"runs-on"`
	Needs  string `yaml:"needs,omitempty"`
	Steps  []Step `yaml:"steps"`
}

type JobState

type JobState map[string]State

type RunArgs

type RunArgs struct {
	Wf *string
	If *string

	Wc *WorkflowConfig
	Ic *InventoryConfig

	Contexts       map[string]map[string]any
	Callback       func(interface{})
	StepOutputType int
}

type RunOption

type RunOption func(*RunArgs)

type Server

type Server struct {
	Name string `yaml:"name"`

	// IP Address or Domain
	Host string `yaml:"host"`

	// SSH Port, defaults to 22
	Port         int    `yaml:"port,omitempty"`
	User         string `yaml:"user"`
	SudoPassword string `yaml:"sudo-pass"`
	SshPassword  string `yaml:"ssh-pass"`

	// File path to the SSH private key
	PrivateSshKey string `yaml:"private-ssh-key"`
}

func (*Server) UnmarshalYAML

func (s *Server) UnmarshalYAML(unmarshal func(interface{}) error) error

Custom UnmarshalYAML to read the private SSH key file

type Ssh

type Ssh struct{}

func NewSsh

func NewSsh() *Ssh

func (*Ssh) Authenticate

func (s *Ssh) Authenticate(args AuthenticateArgs) (*ssh.Client, error)

func (*Ssh) CopyFile

func (s *Ssh) CopyFile(args CopyFileArgs) error

func (*Ssh) CopyTo

func (s *Ssh) CopyTo(client *ssh.Client, source string, destination string) error

Copy file from local server to remote server

@example

ssh := NewSsh()
sshClient, err := ssh.Authenticate(AuthenticateArgs{
	Host:     "10.211.55.12",
	Port:     22,
	User:     "ubuntu",
	Password: "1234567890",
})
fmt.Println(err)
ssh.CopyTo(sshClient, "./from/one/place.yaml", "/to/another.yaml")

func (*Ssh) CreateDirectory

func (s *Ssh) CreateDirectory(sftpClient *sftp.Client, dirPath string) error

func (*Ssh) ExecuteCommand

func (s *Ssh) ExecuteCommand(args ExecuteCommandArgs) (string, string, error)

type State

type State struct {
	IsSuccessful bool
	IsCompleted  bool
	Error        error
}

type Step

type Step struct {
	Name      string `yaml:"name,omitempty"`
	Run       string `yaml:"run,omitempty"`
	Shell     string `yaml:"shell"`
	Directory string `yaml:"directory"`
}

type StepOutputPlain

type StepOutputPlain string

type UninstallArgs

type UninstallArgs struct {
	If string
	Ic InventoryConfig
}

type Workflow

type Workflow struct{}

func NewWorkflow

func NewWorkflow() *Workflow

func (*Workflow) Dump

func (w *Workflow) Dump(content WorkflowConfig) (*string, error)

func (*Workflow) Execute

func (w *Workflow) Execute(args ExecuteArgs) error

func (*Workflow) Load

func (w *Workflow) Load(file string) (*WorkflowConfig, error)

func (*Workflow) Run

func (w *Workflow) Run(opts ...WorkflowRunOptions) error

func (*Workflow) WorkflowWithCallback

func (w *Workflow) WorkflowWithCallback(callback func(any), sot int) WorkflowRunOptions

func (*Workflow) WorkflowWithConfig

func (w *Workflow) WorkflowWithConfig(config WorkflowConfig) WorkflowRunOptions

func (*Workflow) WorkflowWithContexts

func (w *Workflow) WorkflowWithContexts(contexts map[string]map[string]any) WorkflowRunOptions

func (*Workflow) WorkflowWithDirectory

func (w *Workflow) WorkflowWithDirectory(directory string) WorkflowRunOptions

func (*Workflow) WorkflowWithFile

func (w *Workflow) WorkflowWithFile(file string) WorkflowRunOptions

type WorkflowConfig

type WorkflowConfig struct {
	Name string `yaml:"name"`
	On   struct {
		Push        struct{} `yaml:"push"`
		PullRequest struct{} `yaml:"pull-request"`
	} `yaml:"on"`
	Jobs []Job `yaml:"jobs"`

	// Directory to run the workflow from, defaults to the current directory
	Directory string `yaml:"directory"`

	// Defaults provides global fallbacks applied to every job step, such as
	// the shell and directory, unless the step overrides them.
	Defaults Defaults `yaml:"defaults,omitempty"`
}

type WorkflowRunArgs

type WorkflowRunArgs struct {
	File           *string
	Config         *WorkflowConfig
	Contexts       map[string]map[string]any
	Directory      string
	Callback       func(any)
	StepOutputType int
}

type WorkflowRunOptions

type WorkflowRunOptions func(*WorkflowRunArgs)

type WorkflowStepOutputStruct

type WorkflowStepOutputStruct struct {
	// workflow step path
	// 	example; `build.installing curl`, means 👇
	// 	- name: build
	// 		steps:
	// 		- name: installing curl
	// 			run: sudo apt install -y curl
	Path    string
	Command string
	Message string
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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