script

package module
v1.2.6 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 6 Imported by: 0

README

script.go

Write the rest of your deployment pipeline in Go instead of bash.

This GitHub Action compiles your Go code as a Go plugin and executes it inside the workflow. Your script gets a small, batteries-included API for running local commands, running commands on a remote server over SSH, uploading files, and writing env files — no sshpass, scp flags, or heredoc quoting.

Usage

name: Deploy
on:
  push:
    branches:
      - "main"

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: "stable" # needs Go 1.22+

      - name: Run deployment scripts
        uses: alinz/script.go@main
        with:
          workspace: ${{ github.workspace }} # <- this is important
          paths: .github/scripts/deploy # comma-separated if you have more than one
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

Each script lives in its own folder with its own go.mod, and must export a Runner symbol with the signature func(workspace string) error (either func Runner(workspace string) error or var Runner = func(workspace string) error works):

// .github/scripts/deploy/main.go
package main

import (
	"github.com/alinz/script.go"
)

var Runner = func(workspace string) error {
	runner, err := script.NewRunner(&script.Config{
		User: "deploy",
		Host: "example.com",
		// Port: 22,                        // default
		// PrivateKeyEnv: "SSH_PRIVATE_KEY", // default
	})
	if err != nil {
		return err
	}
	defer runner.Close()

	// build locally — full shell semantics, pipes and quoting included
	err = runner.RunLocal(workspace,
		"go build -o ${workspace}/bin/api ./cmd/api",
	)
	if err != nil {
		return err
	}

	// upload artifacts
	err = runner.CopyFiles("0755", "/opt/api", workspace, "bin/api")
	if err != nil {
		return err
	}

	// write an env file on the server (values may reference local env vars)
	err = runner.CreateEnvFile("/opt/api/.env", map[string]string{
		"DATABASE_URL": "${DATABASE_URL}",
		"PORT":         "8080",
	})
	if err != nil {
		return err
	}

	// restart the service
	return runner.RunRemote(
		"sudo systemctl restart api",
	)
}

Because scripts are compiled with -buildmode=plugin, the action requires a Linux (or macOS) runner. Windows runners are not supported.

For a full example repository, see alinz/examples-script.go.

API

Add the library to your script's module:

go get github.com/alinz/script.go
script.NewRunner(config *script.Config) (script.Runner, error)

Validates the config, connects to the remote host, and returns a Runner. Call Close() when done.

type Config struct {
	User             string        // required
	Host             string        // required
	Port             int           // default: 22
	PrivateKeyEnv    string        // default: "SSH_PRIVATE_KEY"
	DefaultLocalPath string        // default: "~/.ssh/id_rsa" (fallback when the env var is unset)
	HostPublicKey    string        // optional: pin the host key (one line of `ssh-keyscan host` output)
	Timeout          time.Duration // default: 30s connection timeout
}

The private key is read from the environment variable named by PrivateKeyEnv; if that is unset, the file at DefaultLocalPath is used. When HostPublicKey is empty, host key verification is disabled — set it in production so a DNS or network hijack can't intercept your deployment.

Runner
type Runner interface {
	RunLocal(workspace string, cmds ...string) error
	RunRemote(cmds ...string) error
	CopyFiles(permissions, remotePath, workspace string, filepaths ...string) error
	CreateEnvFile(path string, env map[string]string) error
	Close() error
}
  • RunLocal runs each command through sh -c, so quoting, pipes, and redirection all work. ${workspace} and any ${ENV_VAR} references are expanded first; unknown references are left for the shell.
  • RunRemote runs commands in a single SSH session, streaming stdout/stderr into the workflow log.
  • CopyFiles uploads files (paths relative to workspace) into remotePath, creating the directory if needed. permissions is an octal mode like "0644" ("" defaults to 0644).
  • CreateEnvFile renders the map as sorted KEY=value lines and uploads it with 0600 permissions. Values may reference local env vars with ${NAME}. The file is transferred over SCP, so values containing quotes, $, or backticks arrive intact.
  • Close releases the SSH connection.

The lower-level SSH client is also available as github.com/alinz/script.go/pkg/ssh with a functional-options constructor (ssh.Client(ssh.WithAddr(...), ssh.WithUser(...), ssh.WithPrivateKey(...), ssh.WithHostKey(...), ssh.WithTimeout(...))).

Upgrading

To use the latest version from main, update your workflow to use @main:

uses: alinz/script.go@main

For stable releases, use the latest version tag (e.g., @v1).

Development

# Install dependencies
go mod download

# Run tests
go test ./...

# Build the action (compiles the script-go CLI)
go build ./cmd/script-go/...

# Test the action locally with a sample script
go run ./cmd/script-go ./path/to/test/script

License

MIT

Documentation

Overview

Package script is the API surface for deployment scripts executed by the script.go GitHub Action. A script builds a Runner once and uses it to run local commands, run remote commands over SSH, copy files, and write env files on the remote host.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v1.2.0

type Config struct {
	// User is the SSH user. Required.
	User string
	// Host is the remote host. Required.
	Host string
	// Port is the SSH port. Defaults to 22.
	Port int
	// PrivateKeyEnv is the name of the environment variable holding the SSH
	// private key. Defaults to "SSH_PRIVATE_KEY".
	PrivateKeyEnv string
	// DefaultLocalPath is the key file used when PrivateKeyEnv is unset.
	// Defaults to "~/.ssh/id_rsa".
	DefaultLocalPath string
	// HostPublicKey optionally pins the remote host key, in authorized_keys
	// format (e.g. one line of `ssh-keyscan host` output). When empty, host
	// key verification is disabled.
	HostPublicKey string
	// Timeout bounds the connection attempt. Defaults to 30 seconds.
	Timeout time.Duration
}

Config describes how to reach the remote host.

type Runner added in v1.2.0

type Runner interface {
	ssh.Runner

	// RunLocal executes cmds sequentially through `sh -c`, so shell quoting,
	// pipes, and redirection work. ${workspace} and ${ANY_ENV_VAR}
	// references are expanded before execution; unknown references are left
	// for the shell.
	RunLocal(workspace string, cmds ...string) error
}

Runner combines local and remote execution. Close it when the script is done to release the SSH connection.

func NewRunner added in v1.2.0

func NewRunner(config *Config) (Runner, error)

NewRunner validates config, connects to the remote host, and returns a Runner. Call Close on the Runner when the script is done.

Directories

Path Synopsis
cmd
script-go command
internal
expand
Package expand implements ${VAR} substitution for commands and env files.
Package expand implements ${VAR} substitution for commands and env files.
plugins
Package plugins loads compiled Go plugins and invokes their exported Runner symbol, which must be a `func(workspace string) error`.
Package plugins loads compiled Go plugins and invokes their exported Runner symbol, which must be a `func(workspace string) error`.
pkg
ssh
Package ssh provides a small SSH client for running remote commands, copying files, and writing env files on a remote host.
Package ssh provides a small SSH client for running remote commands, copying files, and writing env files on a remote host.

Jump to

Keyboard shortcuts

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