spok

module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2023 License: Apache-2.0

README

logo

Spok

License Go Report Card GitHub CI codecov pre-commit.ci status

It's a build system Jim, but not as we know it! 🖖🏻

  • Free software: Apache Software License 2.0

Project Description

spok is a lightweight build system and command runner inspired by things like make, just and others.

On top of this, spok provides:

  • A cleaner, more "developer friendly" syntax
  • Fully cross compatible (tested on Windows, Linux and Mac)
  • Ships with it's own shell interpreter so no dependency on sh, bash, or zsh!
  • Incremental runs based on file hashing and sum checks (not timestamps like e.g. make), so nothing runs if nothing's changed!
  • Incredibly fast execution! Expensive operations are cached, only does any work when it's absolutely necessary
  • Auto loading of .env files
  • Debug info with the --verbose flag
  • An auto spokfile formatter
  • More features TBC

Installation

There are binaries for Mac, Linux and Windows in the GitHub releases section, just download the correct one for your OS and architecture and place it somewhere on $PATH.

For Mac and Linux there is also a homebrew tap:

brew install FollowTheProcess/homebrew-tap/spok

Quickstart

To get started with spok, simply create a spokfile in your project and add a task:

# Run the go tests
task test() {
    go test ./...
}

We also recommend you add the following to your .gitignore:

.spok/

Now on the command line you can run:

spok test

And your tests will be run!

If you want spok to help you out by initialising a demo spokfile (and adding the .spok entry to .gitignore) you can run:

spok --init

The Spokfile

Spok is driven by a single file (usually placed in the root of your project) called spokfile.

The syntax for a spokfile is inspired by a few different things and is intended to be expressive yet very simple:

Makefiles
  • The general structure of a spokfile will be broadly familiar to those who have used make or just before.
  • Tasks (make's targets or just's recipes) are independent declarations. A spokfile can have any number of tasks declared but each must have a unique name.
  • Global variable definitions look similar.
Go
  • Spok borrows quite a bit of Go syntax!
  • Global variables use the := declaration operator.
  • Tasks look kind of like Go functions.
  • Tasks that output multiple things use Go's multiple return syntax.
  • Task bodies are bounded with curly braces.
Python
  • Although in general whitespace is not significant in a spokfile, you'll notice there are no semicolons!
  • Spok looks for line breaks in certain contexts to delimit things.
  • Task outputs make use of the -> operator similar to declaring function return types in Python.
Example

A spokfile looks like this...

# Comments are preceded by a hash

# You can store global variables like this (caps are optional)
# these will also be exported as environment variables available for use
# in any tasks commands
GLOBAL_VARIABLE := "hello"
BIN := "./bin/main"

# You can store the output of a shell command as a variable
# leading and trailing whitespace will always be trimmed off when doing this
GIT_COMMIT := exec("git rev-parse HEAD")

# The core concept in spok is a task (think make target)
# they are sort of based on go functions except arguments are dependencies
# A dependency can be filepaths (including globs) or names of other tasks

# Tasks have optional outputs (if they generate things)
# This enables `spok --clean` to restore everything to it's original state

# Generally, a task is structured like this...

# A line comment above a task is it's docstring
# task <name>(<deps>?...) -> [(]<outputs>?...[)] {
#     command(s) to run
# }

# Some simple examples below

# Use a global variable like this
task hello() {
    echo {{.GLOBAL_VARIABLE}}
}

# Run the go tests (depends on all go source files)
task test("**/*.go") {
    go test ./...
}

# Format the project source code (depends on all go source files)
# if the go source files have not changed, this becomes a no op
task fmt("**/*.go") {
    go fmt ./...
}

# Compile the program (depends on fmt, fmt will run first)
# also outputs a build binary
task build(fmt, "**/*.go") -> "./bin/main" {
    go build
}

# Can also use global variables as outputs
task build2(fmt, "**/*.go") -> BIN {
    go build
}

# Tasks can generate multiple things
task many("**/*.go") -> ("output1.go", "output2.go") {
    go do many things
}

# Can also do glob outputs
# e.g. tasks that populate entire directories like building documentation
task glob("docs/src/*.md") -> "docs/build/*.html" {
    build docs
}

# Can register a default task (by default spok will list all tasks)
task default() {
    echo "default"
}

# Can register a custom clean task
# By default `spok --clean` will remove all declared outputs
# if a task called "clean" is present in the spokfile
# this task will be run instead when `--clean` is used
task clean() {
    rm -rf somedir
}

Benchmarks

Although still in early development, I've benchmarked spok against some very large repos and it performs very well!

For example on the golang/go repo itself with 8872 .go files (at the time of writing) and the following benchmark task:

# Benchmark hashing all go files
task test("**/*.go") {
    echo "I depend on all go files"
}

go_files

Spok is able to hash all 8872 files in just 300ms!

benchmark

Does that mean I can call spok "Blazingly Fast!"? 🤔

Editor Support

There is a VSCode Extension available that provides basic syntax highlighting for spokfiles. It's still in active development so more features TBC!

Directories

Path Synopsis
Package ast defines spok's abstract syntax tree.
Package ast defines spok's abstract syntax tree.
Package builtins implements the built in functions supported by spok, it also exports functions which other packages may use to retrieve and call a builtin function by name.
Package builtins implements the built in functions supported by spok, it also exports functions which other packages may use to retrieve and call a builtin function by name.
Package cache implements spok's mechanism for storing and retrieving the cached SHA256 digest for a spok task.
Package cache implements spok's mechanism for storing and retrieving the cached SHA256 digest for a spok task.
cli
app
Package app implements the CLI functionality, the CLI defers execution to the exported methods in this package
Package app implements the CLI functionality, the CLI defers execution to the exported methods in this package
cmd
Package cmd implements the spok CLI
Package cmd implements the spok CLI
cmd
Package file implements the core functionality to do with the spokfile.
Package file implements the core functionality to do with the spokfile.
Package graph implements a specialised directed acyclic graph (DAG) and the required topological sorting needed for spok's task dependency system.
Package graph implements a specialised directed acyclic graph (DAG) and the required topological sorting needed for spok's task dependency system.
Package hash implements a concurrent file hasher used by spok to detect when task dependencies have changed.
Package hash implements a concurrent file hasher used by spok to detect when task dependencies have changed.
Package iostream provides convenient wrappers around things like stdout, stderr and enables spok to easily talk to a variety of readers and writers.
Package iostream provides convenient wrappers around things like stdout, stderr and enables spok to easily talk to a variety of readers and writers.
Package lexer implements spok's semantic lexer.
Package lexer implements spok's semantic lexer.
Package logger implements an interface behind which a third party, levelled logger can sit.
Package logger implements an interface behind which a third party, levelled logger can sit.
Package parser implements spok's parser.
Package parser implements spok's parser.
Package shell implements spok's command running functionality
Package shell implements spok's command running functionality
Package task handles core spok functionality related to the processing of declared tasks e.g.
Package task handles core spok functionality related to the processing of declared tasks e.g.
Package token declares a number of constants that represent lexical tokens in spok as well as basic operations on those tokens e.g.
Package token declares a number of constants that represent lexical tokens in spok as well as basic operations on those tokens e.g.

Jump to

Keyboard shortcuts

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