Repository navigation
v0.4.0
Where a command runs is now written down — or it does not run at all.
Until now a command execution expression with no $[...] fell back to the host. That fallback is the "works on my machine" failure the language exists to prevent: a deleted declaration or a mistyped program name went silent, and every line had to repeat the image it needed. v0.4.0 removes it, and gives the environment a place to be declared once.
Command declarations and explicit environments
commanddeclares which image provides a program.command "python", "pip" on #python:3.12.14-alpine3.24registers command words for the module; the tasks below then name only what they run:$ pip install -r api/requirements.txt && python -m unittest.- There is no default environment. An expression takes its environment from its
$[...]or from the programs its command string invokes. One that determines neither isE-TYPE-COMMAND-NOENV, caught bylask check. - Dispatch decides only where the text decides. Spec 10.9 scans quoted and nested regions, separators and assignment words, and reports a command word only where the text alone determines it. Selection is unanimity over structural equality —
#localincluded — so a line invoking both a host program and a containerised one isE-TYPE-COMMAND-CONFLICT, never a silent choice. lask cmdruns a declared command by hand.lask cmd go test ./...runs the project's pinned toolchain outside any task, as an argument vector with no shell, attaching the terminal when there is one. Everything after the command name goes to the program verbatim,--helpincluded;lask cmd --listshows what the module declares.- Six new error codes:
E-TYPE-COMMAND-NOENV,-CONFLICT,-DECL,-NAME,-DUPLICATE, alongside the existing-ENV.
Shell completion
lask completion bash|zsh|fishprints a script whose only job is to ask the binary, so an installed script survives upgrades.- It knows your module, not just the CLI.
lask run <TAB>completes the functions the entry module defines — less value bindings,@hidden,internal, and functions taking a positional Environment;lask run build --<TAB>completes that function's keyword parameters;lask cmd <TAB>completes declared commands. kebab and snake spellings mirror each other, and candidates come back in the style being typed. - Nothing is evaluated to answer a
<TAB>. The index parses the entry module and stops there: no elaboration, no import resolution, no dependency cache, no network. Completion works beforedeps synchas ever run, and a module that does not parse still yields declaration heads. A request answers in ~22 ms, so there is no cache to go stale. @completesteers a parameter (spec 3.1): static choices,@keys <binding>reading a top-level map literal without evaluating it,@file [*.ext],@dir.lask run install_completion --shell fishwrites the script into the directory that shell reads and prints the startup line rather than editing anyone's rc file.uninstall_completionis its counterpart.- The README now gives each shell a recipe that works on a stock machine — including that zsh loads nothing from
$fpathuntilcompinithas run, and that bash 3.2, still macOS's/bin/bash, silently reads nothing fromsource <(...).
In the editor
- Semantic tokens paint a command word that carried the environment as the reference it is, inside the command string. Unmatched words stay string text, which makes a bracket-less command with nothing highlighted the visible form of
E-TYPE-COMMAND-NOENV. - Inlay hints show the environment a bare command resolved to, written as the source the author could have written:
$[#node:20.20.2-alpine3.23] cd web && npm ci. A line that already carries$[...]gets no hint, since it says where it runs. - The spans come from elaboration, so the editor shares dispatch's own table and procedure — a word that lights up is a word that voted, and no second, looser matcher exists to drift from it.
exportandinternalare surfaced likeimport: painted as keywords, offered by completion, and read by the token-stream fallback.
Language
- Namespace-qualified type references.
import * as tf from "terraform"now lets an annotation name that module's public type aliases directly:f(): tf.TfOutputs = .... Alias-cycle detection follows qualified edges across modules. - Every string and command chunk carries its source span, so anything downstream can name a location inside a command string.
Breaking changes
- A command execution expression that determines no environment is a static error. Add a
command ... on <Environment>declaration for the programs a module runs, or write$[...]at the site. The examples andmain.laskin this repository show both. run_command(cmd: String, env: Environment)takes the environment as a required positional argument, closing the path by which the core function could still default to the host.exportandinternalare reserved words. A declaration, an identifier-form record field, or an unquoted object key by either name is nowE-SYNTAX-UNEXPECTED-TOKEN; such a field is written{"internal": true}and read asx["internal"]. No module in this repository, lask-terraform or lask-aws was affected.- A
commanddeclaration's environment arguments must be literals: dispatch has to compare two environments structurally, and the environment must be enumerable and pinnable without evaluating the module. - Spec correction in 11.8: standard output is always passed through and only standard error is relayed as the command execution log — the previous wording would have made the output of
lask cmdimpossible to pipe.
Fixes and internals
stripSpansDeclhad noDExportFromcase, so stripping spans from any module usingexport { .. } from ".."crashed at run time.- GHC 9.6.7 → 9.10.3 (Stackage LTS 24.58), with the build now warning-free: partial
headuses replaced, shadowing binds renamed, dead code and unused imports dropped. CoreProgramcarries each module's command table, solask envsandlask env buildaccount for a declared command's image even when no task uses it.
Examples
- example/04-webapp drives Terraform and AWS through the
lask-terraformandlask-awsmodules instead of hand-rolled command strings, and uses two official images —hashicorp/terraformandamazon/aws-cli— in place of a self-built combined Dockerfile, removing thebuild_infra_image()task and its "forgot to call it" failure mode. Exactly one line,test_e2e, still needs an explicit environment: it wants the Playwright image for its browsers rather than for the program it runs. - example/03-terraform bumps lask-terraform to v0.1.1.
- The README says plainly what Lask does not replace: not GitHub Actions, GitLab CI or Jenkins, but what their jobs run — so switching providers rewrites one step rather than the pipeline.
Full Changelog: v0.3.0...v0.4.0