Examples

Real builds, start to finish.

Each example is a complete zuke.ts you can drop into a project and run with ./zuke <target>. They show targets, typed dependencies, tool wrappers, and the $ shell working together.

Node / Astro

Static site build

Drive an npm-based static site through Zuke — this very website is built exactly like this.

zuke.ts
import { Build, run, target } from "jsr:@zuke/core";
import { $ } from "jsr:@zuke/core/shell";
import { NpmTasks } from "jsr:@zuke/npm";

class SiteBuild extends Build {
  clean = target()
    .description("Remove previous build output")
    .executes(async () => { await $`rm -rf dist`.noThrow(); });

  install = target()
    .description("Clean-install from the lockfile")
    .executes(async () => { await NpmTasks.ci(); });

  check = target()
    .description("Type-check the Astro project")
    .dependsOn(this.install)
    .executes(async () => { await NpmTasks.run((s) => s.script("check")); });

  build = target()
    .description("Build the static site into dist/")
    .dependsOn(this.clean, this.check)
    .executes(async () => { await NpmTasks.run((s) => s.script("build")); });
}

if (import.meta.main) await run(SiteBuild);
Deno library

CI gate for a Deno package

Format, lint, type-check, and test with a coverage gate — the kind of pipeline you'd run on every pull request.

zuke.ts
import { Build, run, target } from "jsr:@zuke/core";
import { DenoTasks } from "jsr:@zuke/deno";

class CiBuild extends Build {
  format = target()
    .description("Verify formatting")
    .executes(async () => { await DenoTasks.fmt((s) => s.check()); });

  lint = target()
    .description("Lint sources")
    .executes(async () => { await DenoTasks.lint(); });

  typecheck = target()
    .description("Type-check the public entrypoint")
    .executes(async () => { await DenoTasks.check((s) => s.paths("mod.ts")); });

  test = target()
    .description("Run tests with coverage")
    .dependsOn(this.typecheck)
    .executes(async () => { await DenoTasks.test((s) => s.coverage("cov")); });

  // 'ci' fans out to everything; Zuke runs each prerequisite once.
  ci = target()
    .description("Full CI gate")
    .dependsOn(this.format, this.lint, this.test)
    .executes(() => {});
}

if (import.meta.main) await run(CiBuild);
Docker / release

Build & publish a container

Stamp an image with the current git SHA, build it, and push to a registry — mixing tool wrappers with the $ shell.

zuke.ts
import { Build, run, target } from "jsr:@zuke/core";
import { $ } from "jsr:@zuke/core/shell";
import { DockerTasks } from "jsr:@zuke/docker";

class ReleaseBuild extends Build {
  image = target()
    .description("Build the application image")
    .executes(async () => {
      const sha = await $`git rev-parse --short HEAD`.text();
      await DockerTasks.build((s) =>
        s.tag(`registry.example.com/app:${sha}`).file("Dockerfile")
      );
    });

  publish = target()
    .description("Push the image to the registry")
    .dependsOn(this.image)
    .executes(async () => {
      const sha = await $`git rev-parse --short HEAD`.text();
      await DockerTasks.push((s) => s.image(`registry.example.com/app:${sha}`));
    });
}

if (import.meta.main) await run(ReleaseBuild);

Make it your own

Scaffold a build and start from a working template.