Skip to content
/ garn Public

garn is a build tool and environment manager that replaces justfiles/makefiles, docker, and the annoying parts of READMEs. The builders lingua franca.

License

Notifications You must be signed in to change notification settings

garnix-io/garn

Repository files navigation

The Builders' Lingua Franca

built with garnix License

garn is a build tool and development environment manager. You configure your project with a garn.ts file that declaratively describes your project and its dependencies.

You can think of it as coding up your README. Users, collaborators, and coworkers no longer need to manually follow install steps, or figure out what commands they can run, but instead can just use garn directly. And if it works for you, it works for everyone.1

For example, with this garn.ts file:

import * as garn from "https://garn.io/ts/v0.0.20/mod.ts";

export const frontend = garn.javascript.mkNpmProject({
  description: "My project frontend",
  src: "frontend",
  nodeVersion: "18",
})
  .addExecutable("run", "cd frontend && npm install && npm start");

export const backend = garn.go.mkGoProject({
  description: "My project backend",
  src: "backend",
  goVersion: "1.21",
})
  .addExecutable("run", "cd backend && go run ./main.go");

export const startAll = garn.processCompose({
  frontend: frontend.run,
  backend: backend.run,
});

Anyone can run your frontend with garn run frontend, backend with garn run backend, or both with garn run startAll. All without needing to worry about having the correct version of go, nodejs, or anything else installed.

You may have noticed the use of import URLs in the file above. This is supported by Deno, which we use under the hood. But you don't need to install that either. In fact, garn might just be one of the last things you have to manually install.2

garn is powered by Nix, so you get portable and reproducible builds and development environments.


Table of contents

Getting started

  1. Install garn:

    sh <(curl --proto '=https' --tlsv1.2 -sSf https://garn.io/install.sh)

    garn needs nix to be installed, so if you don't have nix already the above installer will install nix first, after asking for confirmation.

    Manual install (Optional) If you prefer to know exactly what garn is doing, or if you have a special setup not covered by our installation script, you can see this page for manual installation steps.
    Installing shell completion (Optional) You can also install shell completion scripts for the most popular shells. See this page for more information.
    Deno LSP for garn files (Optional) garn is much nicer with an IDE-like experience. You can use garn edit for that or, if you prefer, you can set up your own editor. See this page for more information
  2. Create your first garn.ts file:

    You can use garn init to template out an initial configuration. You can use garn edit to start a text editor.

    Check out the getting started guide for more information on how to write and modify your garn.ts file.

  3. Run some things: Use the garn commands to run some tests, build packages, or run executables.

Commands

garn init

Running garn init in a directory without a garn.ts file will try to automatically detect what kind of project you have and generate a garn.ts file for you.

garn enter

garn enter [project or environment] will put you in a development shell with all needed dependencies available in your $PATH.

garn build

garn build [project] will build the specified project and create a symlink named result which links to the resulting build artifacts.

garn run

garn run [project] will run the default executable for the specified project.

garn check

garn check [check] will run all checks for the specified project. These checks run in a sandbox. The downside of sandboxing is that these checks won't have access to the internet. The upside is that they'll be (almost) perfectly reproducible.

garn edit

garn edit will start (and, if necessary, download) VSCodium, with Deno LSP configured for you. It will open garn.ts in the current directory. This won't clobber any of your existing VSCode/VSCodium configuration and data.

Core concepts

  • Environments (also known as devshells, devenvs, virtualenvs) are the context in which a command runs. That includes: packages that are installed, shell variables that are set, PATH elements. Used both for a devshell experience (think Python virtualenvs) and for giving context to Executables.
  • Packages are recipes for building artifacts (such as binaries or JS bundles). These recipes run in an isolated environment. They also refer to the artifacts themselves.
  • Checks are like Packages, but where we don't care about the artifacts as much as the exit code.
  • Executables are what you already know: bash scripts or binaries that you can run. They have their own Environment, so often can rely on dependencies that are not available "outside".
  • Projects are collections of the above.
  • Plugins are functions that add functionality to a Project, potentially modifying existing Checks, Packages, Executables and Environments, or just adding new ones.

Languages and Stacks



We currently have support for the Npm, Yarn, Go, and Haskell projects, with more languages coming soon. If you really want to see something, make your voice heard in our issue tracker!

Note also our examples directory, which contains tested examples of using garn in various ways.

All examples below require the two following imports:

import * as garn from "https://garn.io/ts/v0.0.20/mod.ts";
import * as pkgs from "https://garn.io/ts/v0.0.20/nixpkgs.ts";

All garn.ts files require the first import, and often you also need the second. Besides that, you rarely need other imports.

Npm

The basic workhorse of Npm projects is mkNpmProject. An example:

export const frontend = garn.javascript.mkNpmProject({
  description: "My project frontend",
  src: ".",
  nodeVersion: "18",
})
  .withDevTools([pkgs.jq])
  .addExecutable("run", "npm install && npm start")
  .addCheck("test", "npm install && npm run test");

This creates a Project containing:

  • A reproducible, CI-ready Check (run it with garn check frontend.test)
  • A no-installation-needed Executable (run it with garn check frontend.test).
  • An Environment or devshell you can enter with garn enter frontend with all dependencies available.

The right version of node, npm, jq, etc. will be used for all these. You can see a full project here.

Go

Go projects are usually built with mkGoProject:

export const server = garn.go.mkGoProject({
    description: "example backend server in go",
    src: ".",
    goVersion: "1.21",
  })
  .addExecutable("migrate", "go run ./scripts/migrate.go")
  .addExecutable("dev", "go run ./main.go");

Haskell

For creating Haskell projects, there's mkHaskellProject. An example:

export const project = garn.haskell.mkHaskellProject({
  description: "My project",
  src: ".",
  executables: ["server", "openapi-generation"]
  ghcVersion: "ghc94"
})

Assuming you have a cabal file in ., this creates a Project containing:

  • A reproducible, CI-ready Package (run it with garn build project) that includes the test-suites of your project.
  • Two executables, that run the corresponding executables from the cabal file. Run them with garn run project.server and garn run project.openapi-generation.
  • An Environment or devshell you can enter with garn enter project with all dependencies available.

The right version of node, npm, jq, etc. will be used for all these. You can see a full project here.

How it works

The essential idea is to generate Nix code corresponding to your projects, and then run Nix behind the scenes. In here, you can see the simple but low-level Nix AST we use. Objects such as Checks have a nixExpression field that contains their corresponding Nix expression.

garn then generates a flake.nix that, for every exported variable of the garn.ts file, has a corresponding package, check, devshell or app.

This means that garn integrates with Nix (and the Nix ecosystem) both ways: you can write pure Nix expressions and embed them in garn.ts files, or import Nix projects, but you can also from Nix call things generated by garn.

Typescript API

You can find documentation for the garn Deno library here.

Comparison to other tools

  • Nix: Nix is a brilliant idea with an immense amount of volunteer effort behind it, and the giant's shoulders on which garn stands. It has a rather complex CLI, and the Nix language is a whole new thing to learn. That said it is a much more mature project with support for just about every imaginable stack. For complex projects, you might be better off picking Nix right now. For simpler ones, or ones that include collaborators who don't feel comfortable with Nix, garn might be a better option.

    But the choice isn't exclusive or binary: garn both allows including Nix expressions or dependencies within it, and generates a flake.nix file that can be consumed by other Nix expressions, so that you can combine Nix and garn quite well.

  • just, taskfile, and make: These are build tools or command runners that help you write and organize the scripts or commands of your project. In that regard, they overlap with garn. They are much easier to install and get started with than garn. But they don't manage packages and environments at all, so every user still needs to figure that out themselves. garn also makes it easy to share your "recipes" as a library. (make also does incremental builds, which garn does not.)

  • virtualenv, rvm, nvm, etc.: These are language-specific tools; very helpful if you need a particular python package or version of python, not at all if you need a system package.

  • devbox and devenv: Both of these projects are more mature than garn; you are likely to find fewer rough corners with them. Other than that, garn differs from them in three major ways: it supports writing your own packages and checks which run in isolated environments (such as CI) and can be cached or consumed by downstream users; it allows configuration in Typescript rather than YAML and/or Nix; and it allows easily writing and importing libraries with reusable functionality.

  • docker: Docker isolates processes, and is often used to distribute dev environments. garn overlaps with this feature. Both tools make it relatively certain that if it works once it works everywhere. But with Docker that comes at the price of harder integration with your development process.

    Besides development environments, Docker is also used for other purposes, such as deployment. garn does not support any of that (yet).

Footnotes

  1. This might not always be true between different architectures and platforms (e.g. Linux vs. MacOS).

  2. That said, the installation of garn itself isn't always smooth on MacOS. If you encounter a problem, please let us know.

About

garn is a build tool and environment manager that replaces justfiles/makefiles, docker, and the annoying parts of READMEs. The builders lingua franca.

Resources

License

Stars

Watchers

Forks

Packages

No packages published