A scripting language built for DevOps and build automation.
Paths, commands, durations, secrets, and tasks as first-class types. Immutable by default, pattern matching throughout, and a standard library that already knows how to talk to git, containers, and SSH.
import std.config
fn changelog_entry(version, notes) {
let lines = notes.map(|n| " - " + n)
"## ${version}\n" + lines.join("\n")
}
@description("Tag and publish a new release")
@deps([test])
@inputs(["Cargo.toml"])
@outputs(["./dist/app"])
task release {
let version = config.read(path("Cargo.toml"))
.unwrap().get_path("package.version")
let tag = `git tag --list v${version}`.stdout.trim()
if tag != "" {
println("Version ${version} already tagged, skipping")
return
}
path("./dist").mkdir()
`cargo build --release`
path("./target/release/app").copy_to(path("./dist/app"))
`git tag v${version}`
println(changelog_entry(version, ["built release", "tagged ${version}"]))
}
Every primitive a build script actually reaches for.
Most scripting languages treat DevOps work as an afterthought — strings you shell out with, and hope for the best. Que starts from the other direction.
Immutable by default
let bindings never change; opt into mutation explicitly with mut. Fewer surprises in a 200-line deploy script.
DevOps-native types
Typed Path and Glob, Duration arithmetic, Semver, and Secret values that redact themselves everywhere — println, logs, and --dry-run alike.
A real task graph
@deps, diamond-safe dependency resolution, and input/output freshness tracking so a task skips when nothing changed — across machines, not just timestamps.
Sandboxing, built in
--allow read=src --allow exec scopes exactly what a script may touch. --dry-run previews every write, command, and HTTP call before it happens.
A stdlib that knows the job
git, HTTP, containers, SSH, archives, checksums, structured logging, file watching — no wrapping subprocess.run in your own retry logic ever again.
One error channel
Result, ?, ?., and command exit codes all raise the same way — a script that ends on an error exits non-zero, which is exactly what CI needs.
Syntax shaped by the problems it solves.
A few lines from the tutorial — not contrived examples, the actual constructs you reach for in a build script.
let root = p"/opt/myapp"
let bin = root / "bin" / "myapp"
for f in glob("src/**/*.rs") {
if f.size() > 1_000_000 {
println("${f}: ${f.size()} bytes")
}
}
let deadline = time.timestamp() + 24h
retry(3, || {
net.wait_for_url(health_url, 20s, 2s)?
})
let token = env.secret("API_TOKEN").unwrap()
`curl -H "Authorization: Bearer ${token}" ${url}`
// --dry-run prints: Bearer <redacted>
@inputs(["src/**/*.rs"])
@outputs(["./target/release/app"])
task compile {
`cargo build --release`
}
// second run: [SKIP] — outputs are already fresh
Install in under a minute.
Que ships as a single statically-linked binary. No package manager, runtime, or interpreter to provision first.
Requires the Rust toolchain (rustup.rs).
git clone https://github.com/jasal82/que-lang.git
cd que
cargo build --release
./target/release/que --help
# optional: put it on your PATH
install -m 755 target/release/que /usr/local/bin/que
Install directly from the git repository with Cargo — no local clone needed.
cargo install --git https://github.com/jasal82/que-lang que
Tagged releases publish prebuilt binaries for Linux, macOS, and Windows as they become available.
# check the latest release
open https://github.com/jasal82/que-lang/releases
Read the tutorial, then write your first task.
Thirty-six chapters, from println("hello") to a full multi-host deployment pipeline with rollback.