Setup Scripts

Automate runner initialization with .forkline/setup.sh and .forkline/setup.rh. Both run after git clone and before the AI agent starts.

Overview

Setup scripts are useful when a repository needs repeatable preparation before work begins:

  • Install dependencies
  • Configure the environment
  • Set up local databases or services
  • Run migrations
  • Prepare tools and utilities

Info: Setup scripts live in your repository, so the setup process is version-controlled alongside the code it supports.

Execution order
01 Phase 01

Clone Repository

The runner checks out the repository root before any setup logic begins.

02 Phase 02

.forkline/setup.rh

Declarative Rash tasks run first when the file is present.

03 Phase 03

.forkline/setup.sh

Imperative Bash glue runs second when the file is present.

04 Phase 04

OpenCode Starts

The interactive agent session continues even if setup logged non-fatal errors.

Repository root working directory5 minute timeout per scriptFailures are logged, not fatal
Forkline always runs setup after clone and before the interactive agent session starts.

Script Types

ScriptFormatBest For
.forkline/setup.rhRash (YAML)Declarative, idempotent setup
.forkline/setup.shBashImperative commands and glue scripts

Bash Scripts (.forkline/setup.sh)

Basic Example

#!/bin/bash
set -euo pipefail

# Install dependencies
npm install

# Run database migrations
npm run db:migrate

# Seed test data
npm run db:seed

Using Secrets

Repository secrets are available as environment variables:

#!/bin/bash
set -euo pipefail

# Secrets are automatically available
echo "Setting up database..."
export DATABASE_URL="$DATABASE_URL"

# Run migrations
npm run db:migrate

# Configure API clients
export STRIPE_API_KEY="$STRIPE_API_KEY"

Warning: Setup script failures are non-fatal. The runner continues to start, but the environment may be incomplete. Check logs if the workspace behaves unexpectedly.

Rash Scripts (.forkline/setup.rh)

Rash is a declarative YAML-based shell language designed for container workflows.

Basic Example

#!/bin/rash
- name: Install dependencies
  command:
    argv: [npm, install]

- name: Run migrations
  command:
    argv: [npm, run, db:migrate]

- name: Create .env file
  copy:
    content: |
      DATABASE_URL={{ env.DATABASE_URL }}
      API_KEY={{ env.API_KEY }}
    dest: .env
    mode: "600"

Script Behavior

PropertyValue
Working directoryRepository root
EnvironmentSecrets are injected as env vars
Timeout5 minutes per script
FailureNon-fatal, logged, runner continues

Debugging Setup Scripts

If your runner behaves unexpectedly, the setup script may have failed. Here’s how to find out what went wrong.

Ask the Runner

When you connect to a runner and notice missing tools or configuration, simply ask the runner about it. Say something like:

“I think the setup script didn’t work. Can you check the setup logs?”

The runner will check /tmp/.forkline-setup.log and report what happened.

What to Look For

Setup script logs contain:

FieldDescription
ScriptWhich script ran (.forkline/setup.rh or .forkline/setup.sh)
InterpreterWhether it was rash or bash
StatusSUCCESS, FAILED, or TIMEOUT
Exit CodeThe numeric exit code if it failed
DurationHow long it took
Secrets InjectedHow many repository secrets were passed
STDOUTOutput from the script
STDERRError messages

Common Issues

Missing tools

Scripts may fail because required CLI tools aren’t installed in the runner:

# Example error in STDERR
npm: command not found
pip: command not found

Solution: Ensure your setup script checks for prerequisites or installs them.

Missing secrets

Scripts expecting environment variables that weren’t configured:

# Example error
$DATABASE_URL: unbound variable

Solution: Add the secret in repository settings before starting the runner.

Permission errors

Scripts trying to write to protected directories:

# Example error
Permission denied: /usr/local/bin/tool

Solution: Write to the workspace directory (.) or use user-writable paths.

Timeouts

Scripts taking longer than 5 minutes:

# Example log entry
Status: TIMEOUT
Duration: 300000ms

Solution: Optimize the script or break it into smaller steps.

Next Steps