sindr

package module
v0.0.12 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2025 License: MIT Imports: 18 Imported by: 0

README

🔨✨ sindr

tag GoDoc Build Status Go report Coverage Contributors License

sindr (from Old Norse: "slag or dross from a forge") is a simple way to create a CLI to save and run project-specific commands.

Using Starlark, a Python-subset, you can create a CLI with flags, arguments and autocompletion to save and run project-specific commands.

Configure your CLI in Starlark, using builtin functions like command and shell to easily create a CLI for your project. Then run sindr to get a CLI for your project-specific commands.

sindr test -- -race
test
  Flags  
    short: true    
  Named arguments  
    args: '-race'    
$ go test -short -race ./...
[...]

sindr has several useful features:

  • sindr generates a fully-fledged CLI giving your developers a familiar interface for how to run scripts.
  • Starlark is a Python-subset which makes configuration simple and familiar for developers.
  • Building with go gives us a single binary with no external dependencies to run.
  • Error messages are clear and if relevant, point to the exact line and column in Starlark.
  • sindr can be invoked from any subdirectory, not just the one with sindr.star.
  • sindr can load .env files, making it easy to populate environment variables.
  • allows string-expansion using golang templates.
  • has a builtin file-based cache system that can be used to check if some command should be run.
  • several other builtin functions to do common tasks like checking if any files have been updated.
  • allows executing arbitrary languages, like Python or NodeJS.

Installation

Installation Script

You can also install sindr using the installation script:

curl -sSL https://github.com/mbark/sindr/raw/master/install.sh | sh

Homebrew

For users of Homebrew, it's probably easiest to install via sindr it:

brew tap mbark/tap
brew install sindr

Pre-built Binaries

You can find pre-built binaries for your platform at GitHub Releases.

macOS
# Intel Mac
curl -sSL https://api.github.com/repos/mbark/sindr/releases/latest | \
  grep "browser_download_url.*darwin_amd64.tar.gz" | \
  cut -d '"' -f 4 | \
  xargs curl -L | tar xz && chmod +x sindr && sudo mv sindr /usr/local/bin/

# Apple Silicon Mac
curl -sSL https://api.github.com/repos/mbark/sindr/releases/latest | \
  grep "browser_download_url.*darwin_arm64.tar.gz" | \
  cut -d '"' -f 4 | \
  xargs curl -L | tar xz && chmod +x sindr && sudo mv sindr /usr/local/bin/
Linux
# 64-bit
curl -sSL https://api.github.com/repos/mbark/sindr/releases/latest | \
  grep "browser_download_url.*linux_amd64.tar.gz" | \
  cut -d '"' -f 4 | \
  xargs curl -L | tar xz && chmod +x sindr && sudo mv sindr /usr/local/bin/

# ARM64
curl -sSL https://api.github.com/repos/mbark/sindr/releases/latest | \
  grep "browser_download_url.*linux_arm64.tar.gz" | \
  cut -d '"' -f 4 | \
  xargs curl -L | tar xz && chmod +x sindr && sudo mv sindr /usr/local/bin/

Using Go

If you have Go installed and prefer using that you can either go install with:

go install github.com/mbark/sindr@latest

When using Go 1.24+ you can also add it as a tool for your project:

go get -tool github.com/mbark/sindr
go mod tidy
go tool sindr

Quick start

Create a file named sindr.star in the root of your project.

cli(
    name = "cli_name",
    usage = "some usage text"
)

def a_command(ctx):
    res = shell(string('echo "{{.text}}"',text=ctx.flags.text))
    print(res.stdout)

command(
    name = "a_command",
    action = a_command,
    flags = {
        "text": {
            "type": "string",
            "default": "hello from sindr",
            "help": "text to echo"
        },
    },
)

Then invoke sindr, it will look in the current directory and upwards for a sindr.star file.

When invoking sindr with no arguments it will by default show the equivalent of running with --help.

$ sindr
NAME:
   cli_name - some usage text

USAGE:
   cli_name [global options] [command [command options]]

COMMANDS:
   a_command  
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --verbose            print logs to stdout (default: false)
   --no-cache           ignore stored values in the cache (default: false)
   --with-line-numbers  print logs with Starlark line numbers if possible (default: false)
   --help, -h           show help

This will behave exactly as a CLI, meaning to run the command a_command just invoke it with that as the argument.

sindr a_command
a_command
  Flags  
    text: 'hello from sindr'    
$ echo "hello from sindr"
  "hello from sindr"  
"hello from sindr"

When running a command sindr will log the name of the command, with flags and arguments if any are defined. In this case none are so it will log simply a_command.

Shell Completion

sindr supports dynamic shell completion for bash, zsh, fish, and PowerShell. The completion automatically adapts to your project's sindr.star configuration, providing context-aware suggestions that work across different projects.

Installation

Fish
# Install completion permanently
sindr completion fish > ~/.config/fish/completions/sindr.fish

# Or source dynamically
sindr completion fish | source
Bash
# For current session
source <(sindr completion bash)

# For permanent installation (Linux)
sindr completion bash > /etc/bash_completion.d/sindr

# For permanent installation (macOS with Homebrew)
sindr completion bash > /usr/local/etc/bash_completion.d/sindr
Zsh
# For current session
source <(sindr completion zsh)

# For permanent installation
sindr completion zsh > ~/.zsh/completions/_sindr

# Make sure your ~/.zshrc includes:
# autoload -U compinit && compinit
PowerShell
# Save to profile
sindr completion powershell >> $PROFILE

# Or for current session
sindr completion powershell | Out-String | Invoke-Expression

How It Works

The completion system is dynamic and context-aware:

  • Project-specific: Completions change based on the sindr.star file in your current directory
  • Always up-to-date: Modifications to sindr.star are reflected immediately without reinstalling
  • Cross-project: Works seamlessly when switching between different projects
  • Global flags: Static global flags (like --verbose, --file-name) are always available

Example

# In project A with commands: build, test, deploy
$ sindr <TAB>
build  test  deploy

# In project B with commands: start, stop, logs  
$ sindr <TAB>
start  stop  logs

Examples

A variety of examples can be found in the examples directory.

Adding commands and subcommands

  • cli
  • command
  • sub_command

Shell commands

  • shell

String templating

  • string

Working with asynchronous commands

  • start
  • wait
  • pool

Working with files

  • newest_ts
  • oldest_ts
  • glob

Using the cache

  • cache

Running other programming languages

  • exec

Importing scripts from pacage.json

  • load_package_json

Sourcing .env files with dotenv

  • dotenv

Working with sindr as a Go-library

sindr was developed out of frustration with existing project-specific commands focusing not being great for the task ( make) or building on the somewhat arcane syntax (just). However, that doesn't mean those are great tools. This section attempts to outline how sindr compares to other popular tools for running commands.

make

make and Makefiles are primarily built for (as the name indicates) building code. The Makefile format specifies what files should be built and what files they depend on. This makes it great for building code but not as good when you primarily use it for running commands. When using Makefiles for commands you can't really require passing arguments or flags and if you want some simple scripting logic like if X then Y the syntax looks arcane.

Consider using make if you want to build code (typically C or C++) and maybe have only a few (.PHONY) command-targets, otherwise a tool specifically for scripts is a better idea.

Just

just is more or less make but optimized for project-specific commands. just is a really nice tool but also suffers from having borrowed much syntax from Makefiles. In many ways it feels like how you wish make worked but it doesn't really re-think the paradigm.

If you're already familiar with Makefiles or Just it's probably the better tool to choose. However, if you're not either sindr allows writing your commands with a subset of Python and you get a CLI – a tool that most developers are already very familiar with how it should be used.

package.json

When working with web or node you can use the script section in your package.json to store common commands to run.

Scripts in package.json files however, are primarily built around having simple one-liner calls, not writing more complex things. If you only have some simple things to do like `prettier --write' or similar, it works great. However, if you need to do more complex logic, you have to create a separate file and write the script there.

package.json is good when you're already working with npm (or similar), have simple one-liners or are fine with creating new files for each command.

Building your own CLI

Pretty much every programming language has some great library for writing your CLI and this is definitely a nice way to do it. It has several upsides: you can use the language the project is already in, you don't need to add any new tooling and you can even re-use some of your other parts of your code base.

However, it can easily feel quite heavyhanded to have to create your own CLI just to run scripts. Additionally, some programming languages are not ideal for scripting and the scripts can become quite cumbersome.

Using a tool developer specifically for running commands – like sindr – can be a good way to allow creating a CLI a bit more easily. Should you also want to add more complex commands, you can use your own file for those. And finally, it's not going to be too hard to migrate from a simple command-runner to your own CLI should you choose to do so.

Inspiration

  • make the original.
  • just like make but explicitly for running commands.

Credit

sindr is primarily an idea built on the shoulder of giants, this is a callout to the great libraries that made this possible.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run(ctx context.Context, args []string, opts ...RunOption) error

Types

type RunOption

type RunOption func(o *runOptions, v *viper.Viper)

func WithBuiltin

func WithBuiltin(name string, builtin StarlarkBuiltin) RunOption

WithBuiltin does exactly what WithGlobalValue does, but handles the much more common case of wanting to add not just any global but specifically a StarlarkBuiltin.

func WithCacheDir

func WithCacheDir(dir string) RunOption

func WithDirectory

func WithDirectory(directory string) RunOption

func WithFileName

func WithFileName(name string) RunOption

func WithGlobalValue

func WithGlobalValue(name string, value starlark.Value) RunOption

func WithLineNumbers

func WithLineNumbers(lineNumbers bool) RunOption

func WithLogger added in v0.0.9

func WithLogger(l logger.Interface) RunOption

func WithNoCache

func WithNoCache(noCache bool) RunOption

func WithVerboseLogging

func WithVerboseLogging(verbose bool) RunOption

func WithWriter added in v0.0.9

func WithWriter(w io.Writer) RunOption

type StarlarkBuiltin

type StarlarkBuiltin = func(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)

StarlarkBuiltin exposes the expected function signature for a starlark builtin function. It's just added here to simplify adding additional Globals.

Directories

Path Synopsis
examples
lib command

Jump to

Keyboard shortcuts

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