Big News!  Tilt is joining Docker

Many Tiltfiles and Many Repos

Most of our example projects use a single Tiltfile. All the source files, Dockerfiles, and Kubernetes configs are in a single folder.

But your project may be organized differently!

How to Organize Your Project for Tilt

Tilt does not require a particular project structure. We try to provide APIs to help you pull the services you need into your dev environment, no matter where they’re defined.

Do NOT restructure your project for Tilt. Instead, restructure your Tiltfiles to match your project structure!

We’ve seen many patterns, including:

  • One main Tiltfile that subincludes a subdirectory Tiltfile for each service.

  • Individual service Tiltfiles that include a library of common functions.

  • Conditional loading of sets of services based on what you have checked out.

  • Loading some services for editing and some services for running read-only.

  • Loading prebuilt services from other repos.

  • Checking out git repos or git submodules from the Tiltfile.

In this doc, we’ll take a tour of the APIs that make these patterns possible.

Loading Library Functions

Suppose you have two services that share dev conventions. Maybe you run them together sometimes! Or maybe you don’t. But you want to factor out common logic for setting up their dev environment.

This is where load() can help. You can use it to load shared constants or functions from a relative path:

load('../common/Tiltfile', 'VERSION', 'common_config_yaml')

print('Loading version: ', VERSION)
k8s_yaml(common_config_yaml())

Loading sub-Tiltfiles

Another pattern is where two services have independent Tiltfiles. But sometimes you want to load them together!

The include() function loads files that don’t export any helper functions.

include('./frontend/Tiltfile')
include('./backend/Tiltfile')

Loading Services Conditionally

We have an example project with an auth service. Sometimes we want to run it with fake user data. Sometimes we want to run it with real GitHub OAuth login. Sometimes we want to run it with HTTPS instead of HTTP.

The load_dynamic() function doesn’t have the nice syntax for loading constants, but does give you the flexibility to conditionally load services.

Here’s an example that uses os.path.exists to see if you have OAuth client secrets or localhost HTTPS certificates. If you do, it uses the real GitHub OAuth login server. Otherwise, it uses fake user data.

USE_OAUTH2 = os.path.exists('../.secrets/values-dev.yaml')
USE_TLS = False
if USE_OAUTH2:
  symbols = load_dynamic('../oauth2-proxy/Tiltfile')
  USE_TLS = symbols['USE_TLS']

Configuring to-run vs to-edit Services

As you have more services, you may need to run subsets of them.

One way to handle this is to have a Tiltfile for each common configuration.

Other teams load all the services in a single Tiltfile, then add controls to enable/disable sets of them.

Tilt has some built-in support for this!

  • tilt enable a b: enable service ‘a’ and ‘b’
  • tilt enable --only a b: enable ‘a’ and ‘b’, and disables all others
  • tilt enable --all: enable all services
  • tilt disable a b: disable ‘a’ and ‘b’
  • tilt disable --all: disable all services

For more complicated configurations, you can define your own flags to tilt up to specify sets of services to run or to edit. For more examples, see the per-user config guide.

Managing other git Repositories

The simplest way to handle multi-repository projects is to ask the user to check out other repos as a sibling directory.

if not os.path.exists('../backend'):
  fail('Please "git clone" the backend repo in ../backend!')
  
include('../backend/Tiltfile')

You can also supply the repository as an environment variable.

backend_dir = os.environ.get('BACKEND_REPO_DIR', '../backend')
if not os.path.exists(backend_dir):
  fail('Please "git clone" the backend repo in %s!' % backend_dir)
  
include(os.path.join(backend_dir, 'Tiltfile'))

Many sophisticated projects use this approach, including the ClusterAPI project for loading servers for each host they deploy to!

Other teams in the Tilt community handle multi-repo projects directly from their Tiltfile. The git_resource extension has functions for checking out arbitrary repos and deploying the resources they contain.

load('ext://git_resource', 'git_checkout')
git_checkout('git@github.com:tilt-dev/tilt-example-html.git#master', '/path/to/local/checkout')

Go to the git_resource README for more details.

Managing Extension Repositories

Finally, Tilt has a set of community-contributed extensions in the tilt-extensions repo.

The community extensions contain both common functions to define your dev environment, and common servers you may want to use.

Many teams manage their own extension repos for extensions that are specific to their orgs! You can read more about how to use them in the Extensions Guide.