Skip to main content

Loop

Start Script in Releezy Loop

Portugues (Brasil)

How to configure the script that prepares the agent's environment to work on your project in Releezy Loop.

This guide explains how to configure the start script — the step that prepares the environment where the Releezy Loop agent will work on your project. It is a usage guide: what you configure, how Releezy calls your script, and how to debug when something goes wrong. It does not describe how Loop works internally — you don’t need that to get the most out of it.

Context: what the “agent environment” isLink to this section

Every time you trigger an agent in Loop (manually or via a webhook from your git repository), use this mental model: the agent starts on a fresh, empty machine, with your repository already cloned, and runs your start script before it begins the task.

That environment is ephemeral: it starts from zero on every run and disappears when it finishes. It does not persist files between runs, has no .env you use locally, no vendor/, no node_modules/, no configured database — none of that.

What it has:

  • The files committed to Git (including .env.example, composer.json, package.json, etc.)
  • Your project’s base languages and tooling, plus whatever you declared in the project configuration
  • The environment variables you configured on the project (tokens, keys)

What it does not have (and your script must provide):

  • Files in .gitignore (.env, vendor/, node_modules/, storage/logs/*)
  • Databases created and migrated
  • Seeds populated
  • App key generated (Laravel) or equivalent
  • Any other setup normally done by an onboarding/deploy step

The start script is where you prepare these. Without it (or with a poorly written one), the agent wakes up on a bare machine and burns turns figuring out how to get things running — by the time the budget runs out, it hasn’t even started the real task.

Where to put the scriptLink to this section

In your repository. Convention: scripts/loop-start.sh (or any path — you tell Releezy where it is).

Then, in Releezy Loop, open your project and under Settings → Start Script enter the relative path, e.g. scripts/loop-start.sh. Save. Done — every new run will execute this script before the agent starts.

How Releezy calls the scriptLink to this section

Releezy runs your script from the root of the cloned repository, in a non-interactive way. You don’t need to know where the repo folder lives or what it’s called. Write the script as if you were running it from the root directory of your own project.

The script’s output (echo, errors, warnings) shows up in the run logs in Releezy. If something goes wrong, that’s where you debug it.

The 4 non-negotiable rulesLink to this section

1. IdempotentLink to this section

The script runs every run. If it reinstalls everything from scratch each time, you burn minutes. Every expensive operation needs an “already done? then skip” check:

# Yes
[ -f "vendor/autoload.php" ] || composer install --no-interaction
[ -f ".env" ] || cp .env.example .env

# No
composer install --no-interaction   # runs always, even when unnecessary
cp .env.example .env                # overwrites existing .env, loses patches

Migrations (migrate, rails db:migrate, etc.) are idempotent by design — run them freely.

2. Self-locatingLink to this section

Never hardcode absolute paths in the script. The name of the folder where Releezy clones the repo is dynamic and can change. Always derive the path from the script’s own location:

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BACKEND_DIR="${REPO_ROOT}/backend"

This works on any machine, at any path.

3. No promptsLink to this section

The agent environment has no interactive terminal. Any command that asks a question hangs the script forever.

Instead ofUse
composer installcomposer install --no-interaction --prefer-dist
npm installnpm ci (more predictable and non-interactive)
php artisan migratephp artisan migrate --force --no-interaction
sudo <cmd>sudo -n <cmd> (fails fast if it asks for a password)

If your tool has a flag like --yes, --silent, --non-interactive, use it.

4. Fail loud on real problems, tolerate redundancyLink to this section

Put set -euo pipefail at the top. This guarantees the script stops immediately if something essential fails.

But for optional things (starting a Postgres that may already be running, creating a DB that may already exist), use || echo "warning..." so you don’t bring everything down:

# Starting Postgres is best-effort — if it's already up, great
sudo -n service postgresql start 2>/dev/null \
  || echo "[start] postgres already running, continuing"

# But installing dependencies is essential — if it fails, stop everything
composer install --no-interaction --prefer-dist

Rule: fail on things that block the agent from working (dependencies, .env, migrations). Tolerate things that are already done (services up, DBs created).

The natural order of thingsLink to this section

  1. Self-locate the script
  2. Start auxiliary services (Postgres, Redis, etc.) — best-effort
  3. Create empty databases — idempotent
  4. Enter the backend directory
  5. Install dependencies (composer, npm) — only if missing
  6. Create .env from .env.example — only if it doesn’t exist
  7. Adjust values in .env if needed (DB host, cache drivers, etc.)
  8. Generate the app key — only if empty
  9. Run migrations
  10. Seed — only if the database is empty
  11. Clear final message: echo "=== [start] environment ready ==="

Variables available inside the scriptLink to this section

Releezy exposes a few variables you can use:

VariableWhat it’s for
$LOOP_REPO_DIRAbsolute path of your repo (already cloned, on the right branch)
$LOOP_AGENT_BRANCHBranch the agent will commit to
$GITHUB_TOKENGit token already configured (to clone private dependencies, if needed)

Use these instead of hardcoded paths.

What NOT to doLink to this section

  • ❌ Hardcoded /workspace/... or absolute paths from your local setup
  • ❌ Interactive commands that ask for confirmation or a password
  • ❌ Reinstalling dependencies from scratch every time
  • ❌ Breaking the whole bootstrap because of one optional service that didn’t come up
  • ❌ Committing .env to the repo — the script creates .env, it does not depend on it already existing
  • sleep 30 “to wait for the DB to come up” — use pg_isready or loops with a timeout
  • ❌ Relying on exotic global tools — use what comes with your project’s environment

How to debug when something goes wrongLink to this section

The start script’s output shows up in the run logs in Releezy. What to look for:

SignalMeaning
Running start script: ...The script was found and started
Lines with your prefix (e.g. [myapp])Which steps ran
WARNING: start script exited with code NThe script failed — check the output above
None of these lines at allThe script configuration is wrong (see Where to put the script)

Signs the agent woke up in a broken environment:

  • It complains about a missing .env
  • It tries to run composer install or npm install mid-task
  • It tries to guess database credentials
  • It spends several turns “setting up the environment”

If this happens, it’s not the agent being lazy — it’s the start script that failed or wasn’t configured. Look at the logs.

To test locally before configuring it in Releezy:

cd <root-of-your-repo>
bash scripts/loop-start.sh

If it runs from scratch (no vendor/, no .env, no DB) and completes without errors, it’s ready. If the second run completes in a few seconds, it’s idempotent.

Minimal template (Laravel)Link to this section

#!/bin/bash
set -euo pipefail

# Self-locate
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BACKEND_DIR="${REPO_ROOT}/backend"

cd "$BACKEND_DIR"

# Dependencies
[ -f "vendor/autoload.php" ] || composer install --no-interaction --prefer-dist
[ -d "node_modules" ] || npm ci

# Env + app key
if [ ! -f ".env" ]; then
  cp .env.example .env
  php artisan key:generate --force --no-interaction
fi

# Migrations
php artisan migrate --force --no-interaction

# Seed only if empty
USERS=$(php artisan tinker --execute="echo \App\Models\User::count();" 2>/dev/null || echo "0")
[ "$USERS" = "0" ] && php artisan db:seed --no-interaction

echo "=== [start] environment ready ==="

Adapt it to your stack:

  • Rails: bundle install + rails db:migrate + rails db:seed
  • Django: pip install -r requirements.txt + python manage.py migrate + python manage.py loaddata
  • Node/Next: npm ci + npx prisma migrate deploy + seed script

The structure is the same.

TL;DRLink to this section

  • The script runs inside a fresh environment, every run, at your repo root
  • Your responsibility: make the environment ready (deps, .env, database)
  • 4 rules: idempotent, self-locating, non-interactive, fail on real problems
  • Configure it under Settings → Start Script in the Loop project
  • If the agent is losing turns setting up the environment, the problem is here — not the agent