Write Dockerfiles and CI pipelines in TypeScript.

Overview

Trellis

Trellis module version Actions status

Write Dockerfiles and CI pipelines in TypeScript.

Trellis is a portable CI/CD tool. With Trellis, you can define your Dockerfiles and CI/CD pipelines in TypeScript, and run them anywhere (locally or on a hosted platform).

Usage

Installation

First, install Deno with brew install deno (or comparable).

Second, install the Trellis CLI with:

deno install \
    --allow-run=docker \
    --allow-net \
    --allow-write \
    --allow-env \
    --allow-read \
    https://deno.land/x/[email protected]/cli.ts

Run trellis --help to verify your installation:

>>> trellis --help
Usage: trellis build mod.ts

Options:
  -V, --version             output the version number
  -h, --help                display help for command

Commands:
  ls [file]                 List all Images and Tasks available in a
                            TypeScript module
  preview [options] [file]  Generate a Dockerfile defined in a TypeScript
                            module
  build [options] [file]    Build an Image defined in a TypeScript module
  run [options] [file]      Run a Task defined in a TypeScript module
  help [command]            display help for command

Define a Docker image

Export any Image to enable Dockerfile generation and image building with Trellis.

For example, to define an Ubuntu image with a few useful utilities installed, you could write the following mod.ts file:

import { Image } from "https://deno.land/x/[email protected]/mod.ts";

const UBUNTU_VERSION = "20.04";

export const buildStage = Image.from(`ubuntu:${UBUNTU_VERSION}`)
  .workDir("/root")
  .aptInstall([
    "curl",
    "jq",
    "git",
  ]);

Running trellis ls mod.ts lists the buildable Images:

>>> trellis ls mod.ts
Images:
- buildStage (trellis build --target buildStage)

We can preview the generated Dockerfile with trellis preview mod.ts --target buildStage:

>>> trellis preview --target buildStage
#syntax=docker/dockerfile:1.4

FROM ubuntu:20.04 AS stage-0
WORKDIR /root
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl git jq

We can build the image with trellis build --target buildStage:

>>> trellis build --target buildStage
[+] Building 0.6s (11/11) FINISHED
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 335B                                                                 0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => resolve image config for docker.io/docker/dockerfile:1.4                                         0.2s
 => CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531bd80fb0a858632727cf7a112fbf  0.0s
 => [internal] load build definition from Dockerfile                                                 0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => [internal] load metadata for docker.io/library/ubuntu:20.04                                      0.2s
 => [stage-0 1/3] FROM docker.io/library/ubuntu:20.04@sha256:35ab2bf57814e9ff49e365efd5a5935b6915ee  0.0s
 => CACHED [stage-0 2/3] WORKDIR /root                                                               0.0s
 => CACHED [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=c  0.0s
 => exporting to image                                                                               0.0s
 => => exporting layers                                                                              0.0s
 => => writing image sha256:17f750ba9a4becf38ce4d584d0de4793bfd6a8139674c3b332cdcdf6525ea8d9         0.0s
 => => naming to docker.io/trellis/db112e211de238c035a9fd3bbcbd5c417aafc5ee96a8c24d99d4caf81a759903  0.0s
√ Build: trellis/db112e211de238c035a9fd3bbcbd5c417aafc5ee96a8c24d99d4caf81a759903

Define a CI/CD pipeline

Export any function from a TypeScript module to enable task execution with Trellis.

For example, to define a CI pipeline to verify that our command-line utilities were successfully installed, you could write the following tasks.ts file:

import { build, Image, run } from "https://deno.land/x/[email protected]/mod.ts";
import { buildStage } from "./mod.ts";

export default async function runChecks() {
  await build(buildStage);

  const checkCurl = Image.from(buildStage).run(
    "curl --help",
  );
  const checkJq = Image.from(buildStage).run(
    "jq --help",
  );
  const checkGit = Image.from(buildStage).run(
    "git --help",
  );

  await Promise.all([
    run(checkCurl),
    run(checkJq),
    run(checkGit),
  ]);
}

Running trellis ls tasks.ts lists the executable Tasks:

>>> trellis ls tasks.ts
Tasks:
- default (trellis run tasks.ts)

We can execute the task locally with trellis run tasks.ts:

>>> trellis run tasks.ts
[+] Building 1.1s (13/13) FINISHED
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 335B                                                                 0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => resolve image config for docker.io/docker/dockerfile:1.4                                         0.5s
 => [auth] docker/dockerfile:pull token for registry-1.docker.io                                     0.0s
 => CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531bd80fb0a858632727cf7a112fbf  0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => [internal] load build definition from Dockerfile                                                 0.0s
 => [internal] load metadata for docker.io/library/ubuntu:20.04                                      0.3s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                        0.0s
 => [stage-0 1/3] FROM docker.io/library/ubuntu:20.04@sha256:35ab2bf57814e9ff49e365efd5a5935b6915ee  0.0s
 => CACHED [stage-0 2/3] WORKDIR /root                                                               0.0s
 => CACHED [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=c  0.0s
 => exporting to image                                                                               0.0s
 => => exporting layers                                                                              0.0s
 => => writing image sha256:17f750ba9a4becf38ce4d584d0de4793bfd6a8139674c3b332cdcdf6525ea8d9         0.0s
 => => naming to docker.io/trellis/adf8a603d1ab539848d89f68491e1b9213c1ca498f3f68d871e1b59c4c7de601  0.0s
√ Build: trellis/adf8a603d1ab539848d89f68491e1b9213c1ca498f3f68d871e1b59c4c7de601
√ Run: git --help
√ Run: jq --help
√ Run: curl --help

Configuration

Trellis can be configured via a trellis.config.ts file, the basic semantics of which are modelled after Vite.

The trellis.config.ts should contain a single default export consisting of a defineConfig invocation, like this:

import { defineConfig } from "https://deno.land/x/[email protected]/mod.ts";

export default defineConfig({
  engine: "docker",
});

Trellis will use the closest trellis.config.ts, looking first in the current working directory, and then in each subsequent parent directory.

Depot

Trellis is compatible with depot.dev, which can be used to enable cloud-accelerated builds with zero configuration. Run through the Depot installation (brew install depot/tap/depot or similar, followed by deplot login), then define a trellis.config.ts like so:

import { defineConfig } from "https://deno.land/x/[email protected]/mod.ts";

export default defineConfig({
  engine: {
    type: "depot",
    project: "${YOUR_PROJECT_ID}",
  },
});

From there, all Trellis builds will run through Depot.

Trellis on GitHub Actions

Trellis runs on Deno, making it a one-step installation on GitHub Actions:

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  DOCKER_BUILDKIT: 1

jobs:
  build:
    name: "Build"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: "Install Deno"
        uses: denoland/setup-deno@v1
        with:
          deno-version: "1.25.2"
      - name: "Install Trellis"
        working-directory: ./trellis
        run: deno install --allow-run=docker --allow-net --allow-write --allow-env --allow-read https://deno.land/x/[email protected]/cli.ts
      - name: "Build the image"
        working-directory: ./examples/typescript
        run: trellis build trellis/mod.ts

Motivation

Trellis is motivated by the following observations, drawn from the experience of maintaining large, containerized CI/CD systems.

  1. Dockerfiles are hard to maintain. Over time, large systems tend to accumulate collections of Dockerfiles with similar subsections, but no shared abstractions.

  2. Efficient Dockerfiles are hard to write. Writing a Dockerfile that's maximally cacheable, with a minimal footprint, requires significant expertise. For example, to apt-get install, the Docker documentation recommends the following:

RUN apt-get update && apt-get install -y \
  # Be sure to sort dependencies to maximize cacheability.
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  # Clear the apt cache to minimize disk size.
  && rm -rf /var/lib/apt/lists/*
  1. The CI/CD iteration loop is too slow. The common workflow for writing a new GitHub Actions pipeline, Jenkinsfile, etc., is to commit, push, wait for the system to acknowledge your change, then wait for your task to fail — tens, or even hundreds of times in a row. With existing CI solutions, you're writing code to run on an unfamiliar system, outside your control, without a first-class development workflow.

  2. CI/CD systems create significant lock-in. Porting your Jenkinsfiles or YAML files to GitHub Actions, or vice versa, requires grappling with platform-specific abstractions.

Trellis solves these problems through a few significant design decisions.

First: With Trellis, you define your Dockerfiles and CI/CD pipelines in TypeScript. This gives you the power of a "full" programming language while retaining a declarative API. With TypeScript, we get the following benefits:

  1. Autocompletion out-of-the-box.
  2. Certain mistakes become "impossible" (e.g., copying non-existent artifacts between stages).
  3. Access to abstraction, modularity, and composability: define common build stages and higher-level primitives to avoid writing complex apt-get install steps by hand.
  4. Immediate access to a rich, familiar ecosystem. Pinging Slack from a CI pipeline is as simple as importing the Slack client from deno.land.

Second: Trellis makes local execution a first-class primitive. CI/CD shouldn't feel like an entirely separate system; it should feel like running code. Trellis is built on Deno and highly portable. You can run trellis build locally just as you would on GitHub Actions or elsewhere. In this way, Trellis takes inspiration from tools like Earthly and Dagger.

Trellis has a few aspirational goals that aren't yet realized:

  • Enable cloud-accelerated builds out-of-the-box. Make cloud-based execution feel local. Kicking off a build locally should resolve to the same compute resources as on the cloud. Builds should initialize in milliseconds.
  • Provide a full CI/CD system out-of-the-box. Define your CI/CD workflows (cron jobs, build triggers, etc.) in code, and deploy them to a hosted Trellis server with zero configuration.

CLI

Trellis is both a library and a command-line interface. With Trellis, you export Image definitions and runnable functions (called "Tasks") from your TypeScript modules, then execute them via the trellis CLI.

trellis preview

Generate a Dockerfile defined in a TypeScript module.

Usage: trellis preview [options] [file]

Generate a Dockerfile defined in a TypeScript module

Options:
  -t, --target <TARGET>  Image to build within the TypeScript module
  -h, --help             display help for command

trellis build

Build an Image defined in a TypeScript module.

Usage: trellis build [options] [file]

Build an Image defined in a TypeScript module

Options:
  -t, --target <TARGET>  Image to build within the TypeScript module
  --push                 Whether to push the image to a remote registry
  -h, --help             display help for command

trellis ls

List all Images and Tasks available in a TypeScript module.

Usage: trellis ls [options] [file]

List all Images and Tasks available in a TypeScript module

Options:
  -h, --help  display help for command

trellis run

Run a Task defined in a TypeScript module.

Run a Task defined in a TypeScript module

Options:
  -t, --target <TARGET>  Task to run within the TypeScript module
  -h, --help             display help for command

Examples

The ./examples directory demonstrates a variety of use-cases for Trellis. Trellis is flexible and can be used solely to generate Dockerfiles for other systems, or for defining entire CI/CD pipelines.

  • rocket: A Rust webserver atop the Rocket framework. Demonstrates multi-stage builds and deployment via Fly.io by leveraging trellis preview.
  • ruff: A Rust command-line tool. Demonstrates efficient builds and CI checks.
  • runc: A Linux development container. Demonstrates generating artifacts with Trellis and copying them back to the host machine.
  • turborepo: Turborepo's own Docker example, modified to generate Dockerfiles with Trellis.
  • typescript: A TypeScript monorepo. Demonstrates efficient builds and CI checks, along with consolidating constants (like the list of TypeScript workspaces).

Architecture

Trellis is built on Deno, which is distributed as a single binary executable with no external dependencies. Using Deno means that installing Trellis anywhere) is as simple as deno install ... — there's no package.json, no npm install, and no TypeScript transpilation step.

Similar to Nixpacks, Trellis generates Dockerfiles. This simplifies Trellis's implementation, but also enables users to leverage Trellis for Dockerfile generation alone, rather than as a complete CI/CD solution.

trellis build and trellis run depend on Docker and assume that the Docker daemon is locally accessible.

Roadmap

  1. Automatically connect to (extremely fast) cloud compute for build and task execution. Make cloud-based builds feel local. (Today, you can accelerate Trellis builds via depot.dev. See: "Depot".)
  2. Create abstractions around "job configuration" and "workflow state", with infrastructure-as-code and one-click deployments, turning Trellis into a standalone CI solution.
  3. Auto-generate Trellis files from source code (like Nixpacks).

License

MIT

You might also like...

Simple format that serves it's one and only purpose and that's creating simple task list everywhere where you can write plain text

Simple format that serves it's one and only purpose and that's creating simple task list everywhere where you can write plain text

SWTF (Simple Worklog Task Format) Simple format that serves it's one and only purpose and that's creating simple task list everywhere where you can wr

Apr 4, 2022

In this project I write the test for several functions and used the TDD with Jest and JavaScript.

JavaScript-Testing-with-Jest npm init -y npm install --save-dev jest Once installed, you should see it in already created Json file Change Jest Script

Jun 11, 2022

MySQL meets Jupyter notebooks. Grasp provides a new way to learn and write SQL, by providing a coding-notebook style with runnable blocks, markdown documentation, and shareable notebooks. ✨

MySQL meets Jupyter notebooks. Grasp provides a new way to learn and write SQL, by providing a coding-notebook style with runnable blocks, markdown documentation, and shareable notebooks. ✨

A New Way to Write & Learn SQL Report Bug · Request Feature Table of Contents About The Project Built With Getting Started Prerequisites Installation

Sep 1, 2022

Chappe - 🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Chappe - 🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Chappe Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine. Chappe is a Developer Do

Jan 1, 2023

A fun and functional way to write regular expressions (RegExp)

funexp A fun and functional way to write regular expressions (RegExp). FunExp is a useful tool for larger projects that depends on RegExp to do heavy

Feb 7, 2022

🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

🧑‍💻 Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine.

Developer Docs builder. Write guides in Markdown and references in API Blueprint. Comes with a built-in search engine. Chappe is a Developer Docs buil

Jan 1, 2023

A CLI to write journal for you and save it to a Notion database!

A CLI to write journal for you and save it to a Notion database!

Journal CLI NPM Link A CLI to write journal for you and save it to a Notion database! Demo Installation Run npm i -g journal-notion-cli to install the

Oct 7, 2022

A JavaScript library to read, write, and merge ZIP archives in web browsers.

Armarius About Armarius is a JavaScript library to read, write, and merge ZIP archives in web browsers. This library mainly focuses on a low memory fo

Nov 9, 2022

A zero-dependency, buildless, terse, and type-safe way to write HTML in JavaScript.

hdot A sensible way to write HTML in JavaScript. Type-safe. Helps you follow the HTML spec. Terse. Almost character for character with plain HTML. Bui

Oct 24, 2022
Releases(v0.0.7)
Owner
Charlie Marsh
Charlie Marsh
Dockerfiles used in PingCAP's docs CI pipeline

openapi-scripts Scripts used in postprocessing OpenAPI document for ReDoc. Postprocess the JSON file generated by bufbuild/buf dereference Use @apidev

Aolin 3 Jul 20, 2022
SEE-EYE is a collection of useful Github actions and workflows used to build CI pipelines for TypeScript applications

SEA-EYE ?? No frils collection of common actions and pre-made workflows for TypeScript project that uses yarn@v1 as package manager. Workflows Build -

Tino Thamjarat 10 Jun 6, 2022
Remix TypeScript monorepo with Turborepo pipelines, Prisma, PostgreSQL, Docker deploy to Fly.io, pnpm, TailwindCSS and Tsyringe for DI.

Remix template with Turborepo, TypeScript and pnpm. The remix app deploys to fly.io or build to Docker image. Example packages for Database with prisma, Tsyringe dependency injection, UI, and internal TypeScript packages.

Philippe L'ATTENTION 33 Dec 29, 2022
A library to create pipelines with contexts and strong type checking.

TypePipe A library to create pipelines with contexts and strong type checking. Installation With Node.js and npm installed in your computer run: npm i

Alvaro Fresquet 16 Jun 11, 2022
Continuous Deployment Pipelines (CI/CD) with Dozer

Continuous deployment pipelines are sets of scripts and tools helping you get your project from your workstation, or version control system, to somewhere your target audience can reach it.

Mirek Kaspar 13 Jan 2, 2023
Learn GraphQL by building a blogging engine. Create resolvers, write schemas, write queries, design the database, test and also deploy.

GraphQL Blog graphqlblog.com Learn GraphQL by building a blogging engine. Create resolvers, write schemas, write queries, design the database, test an

GraphQLApps 6 Aug 17, 2022
Reference for How to Write an Open Source JavaScript Library - https://egghead.io/series/how-to-write-an-open-source-javascript-library

Reference for How to Write an Open Source JavaScript Library The purpose of this document is to serve as a reference for: How to Write an Open Source

Sarbbottam Bandyopadhyay 175 Dec 24, 2022
A Lua plugin, written in TypeScript, to write TypeScript (Lua optional).

typescript.nvim A minimal typescript-language-server integration plugin to set up the language server via nvim-lspconfig and add commands for convenie

Jose Alvarez 315 Dec 29, 2022
Shikhar 4 Oct 9, 2022
This is my to-do list website built with html, css and JavaScript. In this project I used Webpack to bundle JavaScript and ES6 modules to write modular JavaScript.

To-Do-List App This is my to-do list website built with html, css and JavaScript. In this project I used Webpack to bundle JavaScript and ES6 modules

Samuel Mwape 18 Sep 20, 2022