Skip to content

External dependencies ​

Docker, and one network step. The same imports as 08, reaching a module in another repository: lask.json says what a dependency is, the committed lask.lock.json says which bytes it is, and lask sync is the only thing that fetches.

bash
lask sync                    # the only step that touches the network
lask deps list               # what lask.json requests, what the lock pins, and whether they agree
lask deps graph              # the dependency graph
lask eval status

The thing to take away: check, run and eval never reach the network, and an import reaches a dependency's root main.lask and nothing deeper.

This topic has 2 files, so clone the repository to try it:

bash
git clone https://github.com/lask-task-runner/lask.git
cd lask/example/02-language/09-dependencies

Source ​

These are example/02-language/09-dependencies in the lask repository.

lask
// External dependencies
//
// Code worth sharing lives in its own repository and is imported by
// name, not copied. What a dependency is, is declared in lask.json;
// which bytes it is, is pinned in the committed lask.lock.json.
//
//   lask sync             # the only step that touches the network
//   lask deps list        # what lask.json requests, what the lock pins
//   lask deps graph       # the dependency graph
//   lask eval tool-version

// An import with no "./" is an external dependency, and the name is the
// key from lask.json — not a URL, not a path. Nothing in the source
// says where it came from or which revision it is; that belongs in
// lask.json and lask.lock.json, where it can be reviewed and pinned.
import * as tools from "tools"

// The named form works the same way, `as` included.
import { terraform as tf } from "tools"

// A dependency can export command words, which are imported by name
// like anything else: `jq` now runs in the environment tools declared
// for it, as if this module had declared it.
import command { "jq" } from "tools"

// An import reaches a dependency's root main.lask and nothing else:
// `import { x } from "tools/lib/aws.lask"` is a static error
// (E-MODULE-DEEP-IMPORT). A library whose API spans several files
// re-exports it from its own entry module, which is why every
// dependency has exactly one surface.

// The dependency's function returns where terraform runs. Its image is
// the dependency's default unless one is written here, as it is:
// either way it is a head written in some file, so `lask sync`
// pulls it and pins it in this project's lock.
command { "terraform" } on tf(image = #hashicorp/terraform:1.16.4)

// The version of terraform the shared environment runs.
//
// @return The first line of `terraform version`.
tool_version(): String = $ terraform version
  |> lines
  |> first

// Check formatting, through the namespace import.
//
// @param dir  A directory of .tf files.
fmt_ok(--dir: String = "."): Bool = do {
  r = $*[tools.terraform()] terraform fmt -check -recursive #{dir}
  r.code == 0
}

// A value read through the imported command word.
//
// @return The name field of a small JSON document.
name(): String = $ echo '{"name": "lask"}' | jq -r .name
  |> trim

// What the dependency contributes to this module.
//
// A dependency is ordinary code: its functions are called like any
// other, and its types can be named here too.
//
// @return The tool version, and whether this directory is formatted.
status(): Record<version: String, formatted: Bool> = {
  version: tool_version(),
  formatted: fmt_ok()
}

// Nothing here reaches the network. `check`, `run`, `eval` and `envs`
// resolve everything from the fetched tree in .lask/, and a dependency
// that is missing or whose hash does not match the lock is an error
// naming `lask sync` — never a silent fetch in the middle of a run.
json
{
    "dependencies": {
        "tools": {
            "git": "https://github.com/lask-task-runner/lask-module-tools.git",
            "rev": "v0.3.2"
        }
    }
}

Released under the MIT License.