All Projects → xonixx → makesure

xonixx / makesure

Licence: MIT License
Simple task/command runner with declarative goals and dependencies

Programming Languages

shell
77523 projects
awk
318 projects

Projects that are alternatives of or similar to makesure

Walk
A fast, general purpose, graph based build and task execution utility.
Stars: ✭ 108 (-53.04%)
Mutual labels:  build-automation, build-tool, make, build-system
Cargo Make
Rust task runner and build tool.
Stars: ✭ 895 (+289.13%)
Mutual labels:  build-automation, task-runner, build-tool, make
Mmake
Mmake is a small program which wraps make to provide additional functionality, such as user-friendly help output, remote includes, and eventually more. It otherwise acts as a pass-through to standard make.
Stars: ✭ 1,593 (+592.61%)
Mutual labels:  task-runner, build-tool, make, build-system
Flubucore
A cross platform build and deployment automation system for building projects and executing deployment scripts using C# code.
Stars: ✭ 695 (+202.17%)
Mutual labels:  build-automation, build-tool, build-system
rote
Automate everything.
Stars: ✭ 66 (-71.3%)
Mutual labels:  task-runner, build-tool, build-system
Cbt
CBT - fun, fast, intuitive, compositional, statically checked builds written in Scala
Stars: ✭ 489 (+112.61%)
Mutual labels:  build-automation, build-tool, build-system
Ygor
Task toolkit. For when `npm run` isn't enough and everything else is too much.
Stars: ✭ 69 (-70%)
Mutual labels:  task-runner, build-tool, make
make
The Ultimate Makefile to compile all your C, C++, Assembly and Fortran projects
Stars: ✭ 41 (-82.17%)
Mutual labels:  build-tool, make, build-system
Hopp
Crazy rapid build system.
Stars: ✭ 24 (-89.57%)
Mutual labels:  build-automation, build-tool, build-system
obs-docu
Official Open Build Service Documentation. Content gets reviewed and edited. Generated books are available at http://www.openbuildservice.org
Stars: ✭ 26 (-88.7%)
Mutual labels:  build-automation, build-tool, build-system
Zeus
An Electrifying Build System
Stars: ✭ 176 (-23.48%)
Mutual labels:  build-automation, build-tool, make
Earthly
Repeatable builds
Stars: ✭ 5,805 (+2423.91%)
Mutual labels:  build-automation, build-tool, build-system
build
Build system scripts based on GENie (https://github.com/bkaradzic/genie) project generator
Stars: ✭ 30 (-86.96%)
Mutual labels:  build-automation, build-tool, build-system
Foy
A simple, light-weight and modern task runner for general purpose.
Stars: ✭ 157 (-31.74%)
Mutual labels:  task-runner, build-tool, make
alfred
(v0.2) Even Batman needs a little help. Task runner. Automator. Build system.
Stars: ✭ 62 (-73.04%)
Mutual labels:  task-runner, build-tool, build-system
Doit
task management & automation tool
Stars: ✭ 972 (+322.61%)
Mutual labels:  build-automation, task-runner, build-tool
Realize
Realize is the #1 Golang Task Runner which enhance your workflow by automating the most common tasks and using the best performing Golang live reloading.
Stars: ✭ 4,162 (+1709.57%)
Mutual labels:  task-runner, build-tool, build-system
Mask
🎭 A CLI task runner defined by a simple markdown file
Stars: ✭ 495 (+115.22%)
Mutual labels:  task-runner, build-tool, make
Phing
PHing Is Not GNU make; it's a PHP project build system or build tool based on Apache Ant.
Stars: ✭ 1,085 (+371.74%)
Mutual labels:  build-automation, build-tool, make
Build Harness
🤖Collection of Makefiles to facilitate building Golang projects, Dockerfiles, Helm charts, and more
Stars: ✭ 236 (+2.61%)
Mutual labels:  build-automation, build-tool, build-system

makesure

Run tests

Simple task/command runner inspired by make with declarative goals and dependencies.

The simplest way to think of this tool is to have a way to have "shortcuts" (aka goals) to some pieces of scripts. This way allows to call them easily without the need to call long shell one-liners instead.

Example Makesurefile:

@goal downloaded
@reached_if [[ -f code.tar.gz ]]
  wget http://domain/code.tar.gz
  
@goal extracted
@depends_on downloaded
  tar xzf code.tar.gz 

@goal built
@depends_on extracted
  npm install
  npm run build

@goal deployed
@depends_on built
  scp -C -r build/* user@domain:~/www

@goal default
@depends_on deployed

Now to run the whole build you just issue ./makesure command in a folder with Makesurefile (default goal will be called).

You can as well call single goal explicitly, example ./makesure built.

Also pay attention to @reached_if directive. This one allows skipping goal if it's already satisfied. This allows to speedup subsequent executions.

By default, all scripts inside goals are executed with bash. If you want to use sh just add @shell sh directive at start of the Makesurefile.

Features

  • Zero-install
  • Very portable
  • Very simple, only bare minimum of truly needed features. You don’t need to learn a whole new programming language to use the tool! Literally it’s goals + dependencies + handful of directives + bash/shell.
  • Much saner and simpler make analog.
  • A bunch of useful built-in facilities: timing the goal's execution, listing goals in a build file, a means to speed-up repeated builds.
  • The syntax of a build file is also a valid bash/shell (though semantics is different). This can to some extent be in use for editing in IDE.

Usage

$ ./makesure -h
makesure ver. 0.9.17
Usage: makesure [options...] [-f buildfile] [goals...]
 -f,--file buildfile
                 set buildfile to use (default Makesurefile)
 -l,--list       list all available non-@private goals
 -la,--list-all  list all available goals
 -d,--resolved   list resolved dependencies to reach given goals
 -D "var=val",--define "var=val"
                 override @define values
 -s,--silent     silent mode - only output what goals output
 -t,--timing     display execution times for goals and total
 -x,--tracing    enable tracing in bash/sh via `set -x`
 -v,--version    print version and exit
 -h,--help       print help and exit
 -U,--selfupdate update makesure to latest version

Installation

Since makesure is a tiny utility represented by a single file, the recommended installation strategy is to keep it local to a project where it's used (this means in code repository). Not only this eliminates the need for repetitive installation for every dev on a project, but also allows using separate makesure version per project and update only as needed.

wget "https://raw.githubusercontent.com/xonixx/makesure/main/makesure?token=$(date +%s)" -Omakesure && \
chmod +x makesure && echo "makesure $(./makesure -v) installed"

or

curl "https://raw.githubusercontent.com/xonixx/makesure/main/makesure?token=$(date +%s)" -o makesure && \
chmod +x makesure && echo "makesure $(./makesure -v) installed"

Update

Updates makesure executable to latest available version in-place:

./makesure -U

Prerequisites

OS

makesure will run on any environment with POSIX shell available. Tested and officially supported are:

  • Linux
  • MacOS
  • Windows (via Git Bash)

AWK

The core of this tool is implemented in AWK. Almost all major implementations of AWK will work. Tested and officially supported are Gawk, BWK, mawk. This means that the default AWK implementation in your OS will work.

The tool will not work with Busybox awk.

Developed in xonixx/intellij-awk.

Concepts

  • Build file is a text file named Makesurefile.
  • Build file uses directives.
  • Build file consists of a set of goals.
  • A goal is a labeled piece of shell.
  • A goal can declare dependencies on other goals. During execution each referenced dependency will run only once despite the number of occurrences in dependency tree. Dependencies will run in proper sequence according to the inferred topological order. Dependency loops will be reported as error.
  • Goal bodies are executed in separate shell invocations. It means, you can’t easily pass variables from one goal to another. This is done on purpose to enforce declarative style.
  • By default, goals are run with bash. You can change to sh with @shell sh directive specified before all goals.
  • For convenience in all shell invocations the current directory is automatically set to the one of Makesurefile. Typically, this is the root of the project. This allows using relative paths without bothering of the way the build is run.
  • Goal can declare @reached_if directive (link). This allows skipping goal execution if it's already satisfied.

Directives

@options

Only valid: in prelude (meaning before any @goal declaration).

Valid options: timing, tracing, silent

@options timing

Will measure and log each goal execution time + total time.

Example Makesurefile:

@options timing

@goal a
@depends_on b
  echo "Executing goal 'a' ..."
  sleep 1
@goal b
  echo "Executing goal 'b' ..."
  sleep 2

Running:

$ ./makesure a
  goal 'b' ...
Executing goal 'b' ...
  goal 'b' took 2.003 s
  goal 'a' ...
Executing goal 'a' ...
  goal 'a' took 1.003 s
  total time 3.006 s

Small issue exists with this option on macOS. Due to BSD's date not supporting +%N formatting option, the default precision of timings is 1 sec. To make it 1 ms precise (if this is important) just install Gawk (brew install gawk). In this case Gawk built-in gettimeofday function will be used.

@options tracing

Will trace the executed shell script. This activates set -x shell option under the hood.

@options silent

By default makesure logs the goals being executed. Use this option if this is not desired (you only need the output of your own code in goals).

@define

Use this directive to declare global variable (visible to all goals). The variable will be declared as environment variable (via export).

Example:

@define A=hello
@define B="${A} world"

This directive is valid in any place in Makesurefile. However, we recommend:

  • place frequently changed variables (like versions) to the top of Makesurefile
  • place infrequently changed variables closer to the goals/libs that use them

Variable defined with @define can be overridden with a variable passed in invocation via -D parameter.

Overall the precedence for variables resolution is (higher priority top):

  • ./makesure -D VAR=1
  • @define VAR=2 in Makesurefile
  • VAR=3 ./makesure

Please note, the parser of makesure is somewhat stricter here than shell's one:

@define VERSION=1.2.3    # makesure won't accept
@define VERSION='1.2.3'  # OK

@define HW=${HELLO}world    # makesure won't accept  
@define HW="${HELLO}world"  # OK  

@shell

Only valid: in prelude.

Valid options: bash (default), sh

Sets the shell interpreter to be used for execution of goal bodies and @reached_if conditions.

Example:

@shell sh

@goal

Syntax #1:

@goal goal_name [ @private ]

Defines a goal. @private modifier is optional. When goal is private, it won't show in ./makesure -l. To list all goals including private use ./makesure -la.

Lines that go after this declaration line (but before next @goal declaration line) will be treated as a shell script for the body of the goal. Example:

@goal hello
  echo "Hello world" 

Having the above in Makesurefile will produce next output when ran with ./makesure hello

hello world

Indentation in goal body is optional, unlike make, so below is perfectly valid:

@goal hello
echo "Hello world" 

Invoking ./makesure without arguments will attempt to call the goal named default:

@goal default
  echo "I'm default goal"

Syntax #2:

@goal [ goal_name ] @glob <glob pattern> [ @private ]

This one is easy to illustrate with an example:

@goal process_file @glob *.txt 
 echo $ITEM $INDEX $TOTAL

Is equivalent to declaring three goals

@goal [email protected] @private
 echo a.txt 0 2

@goal [email protected] @private
 echo b.txt 1 2
 
@goal process_file
@depends_on [email protected]   
@depends_on [email protected]   

iff

$ ls
a.txt b.txt

For convenience, you can omit name in case of glob goal:

@goal @glob *.txt
 echo $ITEM $INDEX $TOTAL

as equivalent for

@goal a.txt @private
 echo a.txt 0 2

@goal b.txt @private
 echo b.txt 1 2
 
@goal *.txt
@depends_on a.txt 
@depends_on b.txt 

So essentially one glob goal declaration expands to multiple goal declarations based on files present in project that match the glob pattern. Shell glob expansion mechanism applies.

The useful use case here would be to represent a set of test files as a set of goals. The example could be found in the project's own build file.

Why this may be useful? Imagine in your nodejs application you have test1.js, test2.js, test3.js. Now you can use this Makesurefile

@goal @glob test*.js
  echo "running test file $INDEX out of $TOTAL ..."
  node $ITEM

to be able to run each test individually (./makesure test2.js for example) and all together (./makesure 'test*.js').

In case if you need to glob the files with spaces in their names, please check the naming rules section below.

@doc

Only valid: inside @goal.

Provides a description for a goal.

Example Makesurefile:

@goal build
@doc builds the project 
  echo "Building ..."
  
@goal test
@doc tests the project
  echo "Testing ..."

Running ./makesure -l will show

Available goals:
  build : builds the project
  test  : tests the project

@depends_on

Only valid: inside @goal.

Syntax:

@depends_on goal1 [ goal2 [ goal3 [...] ] ]

Declares a dependency on other goal.

Example Makesurefile:

@goal a
  echo a
  
@goal b
@depends_on a
  echo b

Running ./makesure b will show

  goal 'a' ...
a
  goal 'b' ...
b

You can declare multiple dependencies for a goal:

@goal a
  echo a

@goal b
@depends_on a
  echo b

@goal c
  echo c

@goal d
@depends_on b c
  echo d

Running ./makesure d will show

  goal 'a' ...
a
  goal 'b' ...
b
  goal 'c' ...
c
  goal 'd' ...
d

Circular dependency will cause an error:

@goal a
@depends_on b

@goal b
@depends_on c

@goal c
@depends_on a

Running ./makesure a will show

There is a loop in goal dependencies via a -> c

@reached_if

Only valid: inside @goal.

Syntax:

@reached_if <condition>

Allows skipping goal execution if it's already satisfied. This allows to speedup subsequent executions. Only one per goal allowed. The goal will be considered fulfilled (and thus will not run) if condition executed as a shell script returns exit code 0. Any condition evaluation is done only once.

Example Makesurefile:

@goal file_created
@reached_if [[ -f ./file.txt ]]
  echo "Creating file ..."
  echo "hello world" > ./file.txt

If you run ./makesure file_created the first time:

  goal 'file_created' ...
Creating file ...

If you run ./makesure file_created the second time:

  goal 'file_created' [already satisfied].

It is a good practice to name goals that declare @reached_if in past tense.

@lib

Syntax:

@lib [ lib_name ]

Helps with code reuse. Occasionally you need to run similar code in multiple goals. The most obvious approach would be to place a code into shared.sh and invoke it in both goals. The downside is that now you need an additional file(s) and the build file is no more self-contained. @lib to the resque!

The usage is simple:

@lib lib_name
  a() { 
    echo Hello $1  
  }

@goal hello_world
@use_lib lib_name
  a World

For simplicity can omit name:

@lib
  a() {
    echo Hello $1  
  }

@goal hello_world
@use_lib
  a World

Operationally @use_lib is just substituted by content of a corresponding @lib's body, as if the above goal is declared like:

@goal hello_world
  a() {
    echo Hello $1  
  }
  a World

@use_lib

Only valid: inside @goal.

Only single @use_lib per goal is allowed.

Naming rules

It's recommended that you name your goals using alphanumeric chars + underscore.

However, it's possible to name a goal any way you want provided that you apply proper escaping:

@goal 'name with spaces' # all chars between '' have literal meaning, same as in shell, ' itself is not allowed in it

@goal $'name that contains \' single quote' # if you need to have ' in a string, use dollar-strings and escape it

@goal usual_name  

Now ./makesure -l gives:

Available goals:
  'name with spaces'
  $'name that contains \' single quote'
  usual_name

Note, how goal names are already escaped in output. This is to make it easier for you to call it directly:

./makesure $'name that contains \' single quote'

Same naming rules apply to other directives (like @doc).

Usually you won't need this escaping tricks often, but they can be especially in use for @glob goals if the relevant files have spaces in them:

@goal @glob 'file\ with\ spaces*.txt'
@goal other
  @depends_on 'file with spaces1.txt'

More info on this topic is covered in the issue.

Design principles

  • Convention over configuration.
  • Minimalistic. Bare minimum of features that compose good with each other.
  • There should be one way to do the thing.
  • Overall Zen of Python.
  • Think hard before adding new feature. Think of a damage it could cause used improperly. Think of cognitive complexity it introduces. Only add a feature generic enough to cover lots of useful cases instead of just some corner cases. Let's better have a list of recipes for the latter.
  • Do not introduce unjustified complexity. User should not be forced to learn a whole new programming language to work with a tool. Instead, the tool is based on limited set of simple concepts, like goals + dependencies + handful of directives + familiar shell language (bash/sh).
  • Worse is better.
  • Principle of least surprise.
  • Tests coverage is a must.

Omitted features

  • Goals with parameters, like in just
    • We deliberately don't support this feature. The idea is that the build file should be self-contained, so have all the information to run in it, no external parameters should be required. This should be much easier for the final user to run a build. The other reason is that the idea of goal parameterization doesn't play well with dependencies. The tool however has limited parameterization capabilities via ./makesure -D VAR=value.
  • Includes
    • This is a considerable complication to the tool. Also, it makes the build file not self-contained.
  • Shells other than bash/sh
    • Less portable build.
    • If you need to use, say, python for a goal body, it's unclear why you even need makesure at all. Besides, you always can just use python -c "script".
  • Custom own programming language, like make has
    • We think that this would be unjustified complexity.
    • We believe that the power of shell is enough.
  • parallel execution
    • makesure is a task runner, not a full-fledged build tool, like make, ninja or bazel. So if you need one, just use a proper build tool of your choice.

Developer notes

Find some contributor instructions in DEVELOPER.md.

Similar tools

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].