Make your build flow incremental
Non-trivial software projects usually combine multiple technologies, each coming with their specific build tool. The development workflows on such projects (e.g. checking code validity, deploying a new version) involve multiple commands that need to be executed in a coordinated way.
Running these commands manually is prone to errors, as it is easy to forget commands or to run them in the wrong order. On the other hand, using a simple script running all of them systematically is unnecessarily slow.
Žinoma provides a simple command line to execute your most common build flows in the most efficient way.
In particular, Žinoma provides a mechanism to run the tasks incrementally. This means Žinoma will avoid running repetitive tasks again and again if it determines they can be skipped.
It also handles dependencies between tasks (waiting for the completion of one task before starting another one), and runs tasks in parallel whenever possible.
Finally, Žinoma offers a watch mode, which waits for filesystem updates and re-executes the relevant tasks when source files are updated.
Prerequisites:
shell script
brew install fbecart/tap/zinoma
Download the relevant .deb file from the releases page. Then, run:
shell script
dpkg -i zinoma_*.deb
Prerequisites:
shell script
cargo install zinoma
zinoma.yml
)In order to use Žinoma with your project, you need to create a file named zinoma.yml
.
We recommend putting this file in the root directory of your project.
This is the documentation of the format of this file. It assumes prior knowledge of the Yaml format.
targets
A build flow is made of targets. Each target is a unit of work to perform as part of this build flow.
Targets run in parallel by default.
To run targets sequentially, you can define dependencies on other targets using the targets.<target_name>.dependencies
keyword.
targets.<target_name>
Each target must have a name.
The key target_name
is a string and its value is a map of the target's configuration data.
You must replace <target_name>
with a string that is unique to the targets
object.
The <target_name>
must start with an alphanumeric character or _
and contain only alphanumeric characters, -
, or _
.
yaml
targets:
my_first_target:
my_second_target:
In this example:
zinoma my_first_target
will attempt to execute my_first_target
zinoma my_second_target
will attempt to execute my_second_target
zinoma my_first_target my_second_target
will run both targets.targets.<target_name>.dependencies
Identifies any targets that must complete successfully before this target can run. It should be an array of strings.
If a target fails, targets that depend on it will not be executed.
yaml
targets:
target1:
target2:
dependencies: [target1]
target3:
dependencies: [target2]
In this example, target1
must complete successfully before target2
begins, while target3
waits for target2
to complete.
zinoma target2
will run sequentially target1
and target2
.
zinoma target3
will run sequentially target1
, target2
and target3
.
targets.<target_name>.build
Use this keyword to specify the build script of this target. It should be string, which can have one or multiple lines.
yaml
targets:
create_my_file:
build: |
mkdir -p deep/dir
touch deep/dir/my_file
In this example, running zinoma create_my_file
will execute the commands mkdir -p deep/dir
and touch deep/dir/my_file
sequentially.
targets.<target_name>.input_paths
Lists the locations of the source files for this target.
input_paths
should be an array of strings, each representing the path to a file or directory.
The keyword input_paths
enables the incremental build for this target.
This means that, at the time of executing the target, Žinoma will skip its build if its input files have not changed since its last successful completion.
yaml
targets:
npm_install:
input_paths: [ package.json, package-lock.json ]
build: npm install
In this example, running zinoma npm_install
once will execute npm install
.
Subsequent runs of zinoma npm_install
will return immediately — until the content of package.json
or package-lock.json
is modified.
targets.<target_name>.output_paths
This keyword lists the locations where this target produce its artifacts.
Similarly to targets.<target_name>.input_paths
, it should be an array of strings, each representing a path to a file or directory.
If the --clean
flag is provided to zinoma
, the files or directories specified in output_paths
will be deleted before running the build flow.
The incremental build takes in account the output_paths
.
Just like with targets.<target_name>.input_paths
, if any of the target output paths were altered since its previous successful execution, the target state will be invalidated and its build will be run again.
yaml
targets:
npm_install:
input_paths: [ package.json, package-lock.json ]
output_paths: [ node_modules ]
build: npm install
In this example, running zinoma npm_install
will return immediately in case package.json
, package-lock.json
and node_modules
were not modified since the last completion of the target.
Running zinoma --clean npm_install
will start by deleting node_modules
, then will run npm install
.
targets.<target_name>.service
Specifies a command to run upon successful build of the target. It should be a string.
This keyword is meant to enable the execution of long-lasting commands, such as servers.
If the targets to run do not define services, zinoma
will automatically exit after all builds ran to completion.
On the contrary, if at least one target defines a service, zinoma
will keep running even after all builds completed, so that the services can remain alive.
In watch mode (when the --watch
flag is passed to zinoma
), services are restarted every time the target build
runs to completion.
yaml
targets:
npm_server:
input_paths: [ package.json, index.js ]
build: npm install
service: npm start
In this example, zinoma npm_server
will run npm install
and then npm start
.
imports
Use the imports
keyword to import targets from a different project.
It should be an array of strings, each element being a path to another zinoma project.
api/zinoma.yml
:
yaml
targets:
api_test:
build: cargo test
webapp/zinoma.yml
:
yaml
targets:
webapp_test:
build: cargo test
./zinoma.yml
:
```yaml imports: - api - webapp
targets: testall: dependencies: [apitest, webapp_test] ```
In this example, the target test_all
depend from targets defined in different projects.
``` USAGE: zinoma [FLAGS] [OPTIONS] [--] [TARGETS]...
ARGS:
FLAGS: --clean Start by cleaning the target outputs -h, --help Prints help information -V, --version Prints version information -w, --watch Enable watch mode: rebuild targets and restart services on file system changes
OPTIONS:
-p, --project
The incremental build is the core feature of Žinoma. It is meant to accelerate considerably your development environment, while simplifying the execution of your most common build flows.
The best way to speed up your build flow is simply to avoid running its commands. Žinoma helps you do this in a fully automated way.
Targets operate on the file system, transforming some files (aka inputs) into other files (aka outputs).
By looking at the files located in the input_paths
and output_paths
of your targets,
Žinoma can tell if a target needs to run again, or can be skipped.
Žinoma compares files by computing their checksum.
These checksums are stored in the .zinoma
directory, located next to zinoma.yml
.
This directory should be ignored in your version control.
--watch
)Žinoma offers a watch mode which can be enabled with the --watch
option of the command line.
If the watch mode is enabled, zinoma
will not exit after the build flow completion.
Instead, it will keep an eye open on the targets' input_paths
and will re-execute the relevant targets in case filesystem changes are detected.
--clean
)This flag helps you clean up your build environment.
It will delete files specified in your targets.<target_name>.output_paths
and will reinitialize the targets incremental states.
If provided alone, the --clean
flag will clean up all targets of your build flow.
When provided along with targets, the --clean
flag will only run the cleanup on the specified targets and their dependencies.
zinoma
will then proceed to the execution of these targets.
zinoma.yml
:
```yaml targets: downloaddependencies: inputpaths: [ package.json, package-lock.json ] outputpaths: [ nodemodules ] build: npm install
test: dependencies: [ downloaddependencies ] inputpaths: [ package.json, node_modules, src, test ] build: npm test
lint: dependencies: [ downloaddependencies ] inputpaths: [ package.json, node_modules, src, test ] build: npm run lint
check: dependencies: [ test, lint ]
start: dependencies: [ downloaddependencies ] inputpaths: [ package.json, src ] service: exec npm run start
build: dependencies: [ check ] inputpaths: - Dockerfile - package.json - package-lock.json - src outputpaths: [ lambda.zip ] build: | docker build -t build-my-project:latest . docker create -ti --name build-my-project build-my-project:latest bash docker cp build-my-project:/var/task/lambda.zip ./ docker rm -f build-my-project ```
Some example of commands:
zinoma check
will ensure the code complies to the test suites and the coding standards.zinoma start --watch
will run the application and restart it whenever the sources are updated.zinoma --clean build
will generate a clean artifact, ready to be deployed.A fully functional and more advanced example project is available in fbecart/zinoma-node-example.
Žinoma is written in Rust, so you'll need to grab a Rust installation in order to compile it.
To build Žinoma:
shell script
$ git clone git@github.com:fbecart/zinoma.git
$ cd zinoma
$ cargo build --release
$ ./target/release/zinoma --version
Žinoma 0.5.1
To run the test suite, use:
shell script
cargo test
Žinoma is a Lithuanian word. Pronounced it with a stress on the first syllable, which should sound like the gi of regime.
In Lithuanian, žinoma has two meanings:
It is also a recursive acronym for "Žinoma Is NOt MAke!".
This project started as a fork of Steve Mostovoy's buildy.
Žinoma is distributed under the terms of the MIT license.
See LICENSE for details.