Harmont docs
Examples

Next.js

A Next.js app via the js toolchain: build, test, and lint.

A Next.js app via the js toolchain: build, test, and lint.

Project layout

.eslintrc.json
.gitignore
jest.config.js
next.config.js
package-lock.json
package.json
README.md
tsconfig.json

Pipeline

.hm/pipeline.py
"""Next.js example pipeline."""from __future__ import annotationsimport harmont as hm@hm.pipeline(    "ci",    env={"CI": "true"},    default_image="ubuntu:24.04",    triggers=[hm.push(branch="main")],)def ci() -> tuple[hm.Step, ...]:    project = hm.js.project(path=".")    return (project.run("build"), project.run("test"), project.run("lint"))
.hm/pipeline.ts
import { pipeline, push, type PipelineDefinition } from "@harmont/hm";import { js } from "@harmont/hm/toolchains";const project = js.project({ path: "." });const pipelines: PipelineDefinition[] = [  {    slug: "ci",    triggers: [push({ branch: "main" })],    pipeline: pipeline([project.run("build"), project.run("test"), project.run("lint")], {      env: { CI: "true" },      defaultImage: "ubuntu:24.04",    }),  },];export default pipelines;

Run it

hm run ci

Before your first run: pin your package manager

create-next-app installs your dependencies with a specific pnpm version and writes a supply-chain policy (minimumReleaseAge, the 24-hour package-age gate) next to the lockfile. The trouble starts when the build runs a different, newer pnpm than the one that scaffolded the project: pnpm 11.1+ re-validates the committed lockfile against that policy before installing, and a brand-new app pins the freshest Next.js, React, and type packages — all published in the last day — so the install fails before fetching anything:

✗ Lockfile failed supply-chain policy check
[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 5 lockfile entries failed verification:
  caniuse-lite@1.0.30001799 was published ... within the minimumReleaseAge cutoff

The fix is to build with the same pnpm that scaffolded the project, so CI resolves exactly what you ran locally. create-next-app prints that version on its last line:

Done in 5.4s using pnpm v10.33.0

Add a matching packageManager field to package.json:

package.json
{
  "packageManager": "pnpm@10.33.0"
}

The js toolchain reads this field and pins the build's pnpm to it (via corepack), so CI installs with the same package manager as your machine. The build becomes reproducible and your lockfile's supply-chain policy stays intact — no need to weaken it.

Use the exact version from your own create-next-app output — it may differ from 10.33.0. Pinning packageManager is good hygiene for any JS project on Harmont: it's what makes your local install and your CI install byte-for-byte the same.

Stuck?

Pipelines for real apps hit real edges. If something doesn't build the way you expect, ask in the Harmont Discord — we answer fast.

On this page