fuigo

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: MIT Imports: 20 Imported by: 0

README

fuigo

go install with pre-build steps.

Why

go install cannot run go generate or any pre-build step, so projects that need code generation or a frontend build must either commit generated artifacts or document a multi-step manual build. fuigo is a drop-in go install replacement that runs a module's declared pre-build steps first. It is a temporary solution until Go adds native pre-build support.

Install

go install github.com/sopranoworks/fuigo/cmd/fuigo@latest

Usage

fuigo <package>[@version]
fuigo github.com/sopranoworks/shoka/cmd/shoka@latest

If the target module has a fuigo.yaml at its root, fuigo runs the declared steps before go install. If it does not, fuigo behaves exactly like go install.

Flags:

  • -t, --check — validate fuigo.yaml without executing, then exit
  • --dry-run — run the pre-build steps and go build, but skip go install
  • --yes — skip the confirmation prompt (for CI)
  • --list — show the steps without executing them
  • --version — print the fuigo version

There are three levels of verification, each doing strictly more than the last:

Command Validate config Run steps go build go install
fuigo -t <target>
fuigo --dry-run <target>
fuigo <target>

--dry-run runs every pre-build step (npmgo download, esbuild bundle, go generate, …) and then compiles the target with go build — confirming that the repository state produces a clean build — but stops before installing, so no binary is written to GOBIN. It works on both local and remote targets, and still prompts for confirmation unless --yes is given.

Local directory install

fuigo can install from a local directory instead of fetching from the module proxy — handy during development. A target is treated as local when it starts with ., .., or /, or when its first path segment has no dot (i.e. it is not a host.tld/... module path).

fuigo .                  # install from the current directory (auto-detects ./cmd/*)
fuigo ./path/to/module   # install from a relative path
fuigo . ./cmd/shoka      # install a specific package

With no explicit package, fuigo installs every ./cmd/* package that has a main.go. Steps (including their workdir) resolve against the local path.

Validate without running: -t

Like httpd -t, fuigo -t checks a fuigo.yaml and reports every problem without executing anything. It works on local paths and remote modules (the module is fetched, validated, and cleaned up).

fuigo -t .                                         # validate the local config
fuigo -t github.com/sopranoworks/shoka/cmd/shoka@latest
fuigo: fuigo.yaml syntax OK (2 steps)
fuigo: fuigo.yaml error: step 2: command "npm install" not allowed (must start with go/npmgo/esbuild)

A missing fuigo.yaml is reported, not an error (fuigo falls back to plain go install).

fuigo.yaml

A list of pre-build steps, run in order from the module root:

steps:
  - npmgo install --cache-only --lockfile web/package-lock.json
  - esbuild --entry web/src/main.tsx --bundle --outdir server/dist/
  - go generate ./...

Steps must start with one of three commands:

  • go — runs the external go tool
  • npmgo — installs npm packages (built-in, no Node.js required)
  • esbuild — bundles TS/JSX/CSS (built-in)

npmgo and esbuild are compiled into fuigo, so a single go install fuigo gives you npm install + bundling + Go build orchestration. No external commands other than go are ever executed.

Running a step in a subdirectory

A step may be written as a map with a workdir (relative to the module root) instead of a bare string. fuigo runs the command with that directory as its working directory — there is no shell, so cd …&& … does not work. The workdir must stay within the module root.

steps:
  - command: go run .
    workdir: build/frontend
  - go generate ./server/...

How it works

  1. Resolve the module version and download its source zip from the Go module proxy (proxy.golang.org) — the same mechanism as go install, no git. Private modules fall back to a git clone.
  2. Extract to a temporary directory.
  3. Read fuigo.yaml, show the steps, prompt for confirmation.
  4. Run the steps, then go install.
  5. Clean up the temporary directory.

Security

Steps are defined by the module author, in the repository — the same trust model as go install, which already compiles and runs the module's code. fuigo shows the steps and asks before running them; --yes skips the prompt. Only go, npmgo, and esbuild steps are permitted.

License

MIT © 2026 Sopranoworks, Osamu Takahashi

Documentation

Overview

Package fuigo wraps "go install" with optional pre-build steps. It reads a fuigo.yaml from the target module (fetched from the Go module proxy), runs the declared steps, then delegates to go install. Without a fuigo.yaml it behaves exactly like go install.

Index

Constants

View Source
const ConfigFile = "fuigo.yaml"

ConfigFile is the name of the per-module configuration file fuigo looks for.

Variables

This section is empty.

Functions

func Build added in v0.2.2

func Build(dir, relPkg string) error

Build compiles the package at relPkg within the module source rooted at dir without producing a binary (output is discarded via os.DevNull), to verify the code compiles. It is used by dry-run mode in place of go install. relPkg follows the same convention as Install: an empty relPkg builds the module root package. Output is streamed live.

func Check added in v0.2.0

func Check(opts Options) error

Check validates the target's fuigo.yaml without executing anything. For a remote target it fetches the module zip, validates, and cleans up. It returns a non-nil error when validation fails (problems are reported via Logf).

func ExecuteSteps

func ExecuteSteps(root string, steps []Step, builtins Builtins) error

ExecuteSteps runs each step with root as the module root. A step is dispatched by its first token: "go" runs the external go tool; "npmgo" and "esbuild" invoke the built-in libraries. Any other command is rejected. When a step sets a Workdir it runs in root/Workdir, which must stay within root. Execution stops at the first failing step, returning an error that names it.

func Install

func Install(dir, relPkg string) error

Install delegates to "go install" for the package at relPkg within the module source rooted at dir. relPkg is the package path relative to the module root (e.g. "cmd/shoka"); an empty relPkg installs the module root package. Output is streamed live.

func InstallDir

func InstallDir() string

InstallDir reports the directory go install writes binaries to: GOBIN if set, otherwise GOPATH/bin.

func ResolveModule

func ResolveModule(pkg, version string) (srcDir string, resolved string, cleanup func(), err error)

ResolveModule fetches the source of the module containing pkg at the given version and extracts it to a temporary directory. version may be "", "latest" or an explicit "vX.Y.Z". It returns the extracted source directory, a cleanup function that removes it, and the resolved concrete version.

The primary path downloads a zip from the Go module proxy (no git). If the module is not available on the proxy (e.g. GOPRIVATE repositories) it falls back to a shallow git clone via go-git.

func Run

func Run(opts Options) error

Run runs any pre-build steps for the target and installs it. The target is either a remote module (fetched from the proxy) or a local directory. It is the entry point used by the CLI.

func SplitModulePath

func SplitModulePath(pkg string) (module, relPkg string)

SplitModulePath splits a full package import path into its module root and the package path relative to that root. For the common VCS hosts the module root is host/owner/repo; e.g. "github.com/sopranoworks/shoka/cmd/shoka" becomes module "github.com/sopranoworks/shoka" and relPkg "cmd/shoka".

func ValidateConfig added in v0.2.0

func ValidateConfig(dir string) (found bool, steps int, problems []string)

ValidateConfig checks the fuigo.yaml in dir without executing anything. It reports whether a config is present, the step count, and every problem found (not just the first). A missing fuigo.yaml is reported as found=false with no problems — that is a valid state (plain go install).

Types

type Builtins

type Builtins struct {
	Npmgo   func(dir string, args []string) error
	Esbuild func(dir string, args []string) error
	Logf    func(format string, args ...any)
}

Builtins holds the implementations fuigo dispatches built-in steps to. They are injectable so tests can substitute fakes. Logf, when set, receives progress messages; a nil Logf discards them.

func DefaultBuiltins

func DefaultBuiltins(logf func(format string, args ...any)) Builtins

DefaultBuiltins returns Builtins wired to the compiled-in npmgo and esbuild libraries, logging progress through logf.

type Config

type Config struct {
	Steps []Step `yaml:"steps"`
}

Config is the parsed contents of a module's fuigo.yaml.

func LoadConfig

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

LoadConfig reads fuigo.yaml from dir. If the file does not exist it returns (nil, nil): a module without a fuigo.yaml is valid and means "plain go install". Any present config is validated: steps must be non-empty and every step must start with one of the allowed commands.

type Options

type Options struct {
	// Package is the target: a remote module path (optionally with an
	// "@version" suffix, e.g. "github.com/sopranoworks/shoka/cmd/shoka@latest")
	// or a local directory (".", "./path", "/abs/path").
	Package string
	// Subpkg optionally names the package to install for a local target, e.g.
	// "./cmd/shoka". When empty, local installs auto-detect ./cmd/*.
	Subpkg string
	// Yes skips the confirmation prompt before executing steps.
	Yes bool
	// List prints the pre-build steps without executing anything.
	List bool
	// DryRun executes all pre-build steps but skips the final go install.
	DryRun bool

	// Logf receives progress messages (without a trailing newline). If nil,
	// messages are discarded.
	Logf func(format string, args ...any)
	// In is the source of the confirmation answer; defaults handled by caller.
	In io.Reader
	// Out is where the confirmation prompt is written.
	Out io.Writer
}

Options configures a Run or Check.

type Step added in v0.1.1

type Step struct {
	Command string
	Workdir string
}

Step is a single pre-build step. It is either a bare command (string form) or a command plus a workdir (map form). Workdir is a path relative to the module root; an empty Workdir means the module root itself.

func (Step) String added in v0.1.1

func (s Step) String() string

String renders a step for display, noting the workdir when present.

func (*Step) UnmarshalYAML added in v0.1.1

func (s *Step) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts a step as either a scalar string ("go generate ./...") or a mapping ({command: ..., workdir: ...}).

Directories

Path Synopsis
cmd
fuigo command
Command fuigo is a drop-in "go install" replacement that runs a module's declared pre-build steps (from fuigo.yaml) before installing.
Command fuigo is a drop-in "go install" replacement that runs a module's declared pre-build steps (from fuigo.yaml) before installing.

Jump to

Keyboard shortcuts

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