Skip to main content
Version: Next

Adopt your existing tools

Start with one command that already works in your repository. Terrabuild will coordinate it with other work; your native project files remain the source of truth for compilation, dependencies, and tool-specific options.

You can scaffold recognized projects or write the first WORKSPACE and PROJECT yourself. In either case, begin with one useful target and check its outputs before extending the graph.

1. Wrap a familiar build

For an existing .NET project, put this PROJECT beside its project file:

src/api/PROJECT
project api {
@dotnet { }
}

target build {
@dotnet build { configuration = "Release" }
}

At the repository root:

WORKSPACE
workspace { }

target build {
depends_on = [ target.^build ]
}

The two uses of @dotnet have different roles. Inside project, its initializer reads native project metadata, including dependencies and outputs. Inside target build, its action describes the command to execute.

The .NET SDK must be available on the host, or you can configure a container image for the extension. An included extension supplies the integration; it does not install the native tool on your machine.

From the repository root:

terrabuild explain build --project api
terrabuild run build --project api

Check the compiled files, then repeat the command to check reuse. If the native project references another project, add a PROJECT there too so Terrabuild can represent the dependency. Give it a build target with the appropriate action.

2. Add another toolchain

A frontend with existing install and build requirements can use npm:

src/web/PROJECT
project web {
outputs = [ "dist/**" ]
@npm { }
}

target build {
@npm install { }
@npm build { }
}

This assumes package.json has a build script that writes to dist/. Adjust the outputs to match your application. Commands inside one target run in their declared order; independent project targets can run concurrently.

terrabuild run build

Now the shared target name coordinates two different tools. It does not require them to share a package manager or a build language.

3. Describe dependencies the tools cannot discover

Native project references cover some relationships. Others cross tool boundaries: for example, a web application may consume a client generated from an API schema. Add a project dependency for that relationship, then name the required target.

project web {
depends_on = [ project.client ]
outputs = [ "dist/**" ]
@npm { }
}

target build {
depends_on = [ target.^generate ]
@npm install { }
@npm build { }
}

Here, the client project must expose a generate target. The workspace's target.^build rule and this project's target.^generate rule are combined. See generated code for the complete relationship.

4. Check what may be reused

Before scaling up, verify three things:

CheckWhy it matters
Source files and tool configuration are tracked inputs.A change must invalidate the affected result.
Output patterns match generated artifacts.Terrabuild must know which files to save and restore.
Commands have appropriate artifact and build policies.A publish or deployment action may need different treatment from compilation.

Try a second run, check restoration through a consuming target that executes, then change a source file. These are the same experiments as the first tutorial. They establish that the model works before it becomes a dependency for more work.

Included extensions supply defaults, but they cannot know every custom output path or external input. Use target policies to make those choices explicit.

5. Move the same request into CI

After checkout and tool installation, CI can invoke:

terrabuild run build

Keep repository relationships in WORKSPACE and PROJECT. Keep event triggers, runner provisioning, credentials, and approvals in CI. Add Insights when you want artifact reuse across machines and an execution history.

Next, configure environments or customize the included integrations.