Page 2 of 5

The .cicd Folder

The .cicd/ directory in your repository root houses the execution hooks that CICD runs to install dependencies and boot your application.

Core Best Practice: Separating Installs & Builds

Install script (install.sh): Use strictly for downloading packages or system libraries. This script is hash-cached and only runs when its file content changes.
Run script (run.sh): Use for starting the server and running compilation/build steps (like npm run build, next build, or go build). Since run.sh executes on every single webhook push, this ensures your application is compiled with the latest commits on every deploy.

Folder Structure

your-project/
├── .cicd/
│   ├── install.sh      ← Download dependencies & packages (runs on content change)
│   ├── run.sh           ← Compile/build & start your application (runs every deploy)
│   ├── health.sh        ← (Optional) Custom health verification loop
│   └── timeout          ← (Optional) Deploy timeout override in seconds
├── src/
├── package.json
└── ...

install.sh — Dependency Downloads

Use install.sh to download external modules. It executes under a sandboxed system account with temporary NOPASSWD sudo access for package managers.

Node.js Example

#!/bin/bash
echo "📦 Downloading node modules..."
# Use ci (clean install) for quick and predictable package setups
npm ci --production

Python Example

#!/bin/bash
echo "📦 Building Virtualenv..."
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

Go Example

#!/bin/bash
echo "📦 Downloading Go dependencies..."
go mod download

Rust Example

#!/bin/bash
# Install cargo compiler if missing
if ! command -v cargo &> /dev/null; then
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source "$HOME/.cargo/env"
fi

run.sh — Build & Start Application

run.sh executes on **every deployment** after a git fetch completes. Place your compilers, bundlers, transpilers, and start commands here.

Automatic Port Bind Cleanup

Before running run.sh, the CICD runner automatically scans your project config, identifies the active port, and kills any existing process listening on it. You do not need to clean up ports manually in your script.

Node.js/Next.js Build & Start

#!/bin/bash
# 1. Compile project on every deploy
echo "🔨 Running webpack/next build..."
npm run build

# 2. Launch server in foreground (Tier 1)
echo "🚀 Starting Node server..."
node dist/index.js

Python Web App

#!/bin/bash
source venv/bin/activate

# 1. Run migrations on every deploy
python manage.py migrate

# 2. Start server
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000

Go Compile & Run

#!/bin/bash
# 1. Compile on every deploy
go build -o app ./cmd/server

# 2. Run binary
./app

Rust Compile & Run

#!/bin/bash
# 1. Build release binary
cargo build --release

# 2. Run binary
./target/release/my-rust-app

Running Tests on Every Deploy

If you want to run automated test suites (unit tests, security checks, linting) before your code is built and deployed, you should trigger them directly in your run.sh file. Because run.sh runs synchronously on every webhook push, if any command returns a non-zero exit code, the deployment fails, and the orchestrator automatically triggers a rollback to the previous stable commit.

Node.js Test & Build Example

#!/bin/bash
# 1. Run unit/integration tests first
echo "🧪 Running test suites..."
npm test || { echo "❌ Tests failed! Aborting deployment."; exit 1; }

# 2. Compile/build project on success
echo "🔨 Running build..."
npm run build

# 3. Launch application server
echo "🚀 Starting Node server..."
node dist/index.js

Go Test & Build Example

#!/bin/bash
# 1. Run test suite
echo "🧪 Testing Go packages..."
go test ./... || { echo "❌ Go tests failed! Aborting deployment."; exit 1; }

# 2. Compile on success
go build -o app ./cmd/server

# 3. Run binary
./app

Production-Ready Project Boilerplates

We provide fully configured boilerplate directories for standard frameworks under the docs/examples/ directory of the repository. You can explore, host, or reference these directly on GitHub:

📂 GitHub Path: docs/examples/nodejs-pm2/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing Node.js environment..."

# 1. Install Node.js/NPM if missing
if ! command -v node &> /dev/null; then
  echo "📥 Node.js is missing. Installing Node.js & NPM..."
  if command -v apt-get &> /dev/null; then
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt-get install -y nodejs
  elif command -v yum &> /dev/null; then
    curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
    sudo yum install -y nodejs
  fi
fi

# 2. Install PM2 if missing
if ! command -v pm2 &> /dev/null; then
  echo "📥 PM2 is missing. Installing PM2 globally..."
  sudo npm install -g pm2
fi

echo "🌸 Starting dependencies installation..."
npm ci --production
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# Ensure dependencies exist before running
if ! command -v node &> /dev/null; then
  echo "❌ Error: Node.js is not installed! Aborting run."
  exit 1
fi
if ! command -v pm2 &> /dev/null; then
  echo "❌ Error: PM2 is not installed! Aborting run."
  exit 1
fi

echo "🧪 Running unit tests..."
npm test || { echo "❌ Unit tests failed! Aborting deployment."; exit 1; }

echo "🔨 Running build..."
npm run build --if-present

echo "🚀 Deploying with PM2..."
pm2 delete "$CICD_SERVICE_NAME" 2>/dev/null || true
pm2 start dist/index.js --name "$CICD_SERVICE_NAME"
pm2 pid "$CICD_SERVICE_NAME" > "$CICD_PID_FILE"
📄 .cicd/health.sh bash
#!/bin/bash
set -e
curl -sf http://localhost:3000/health || exit 1
📂 GitHub Path: docs/examples/python-flask/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing Python environment..."

# 1. Install Python 3 if missing
if ! command -v python3 &> /dev/null; then
  echo "📥 Python 3 is missing. Installing Python 3 & pip..."
  if command -v apt-get &> /dev/null; then
    sudo apt-get update -y
    sudo apt-get install -y python3 python3-pip python3-venv
  elif command -v yum &> /dev/null; then
    sudo yum update -y
    sudo yum install -y python3 python3-pip
  fi
fi

echo "🌸 Initializing Python virtual environment..."
# 2. Create a virtual environment inside 'venv' if it doesn't exist
if [ ! -d "venv" ]; then
  python3 -m venv venv
fi

# 3. Activate virtualenv and download packages
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# Ensure dependencies exist before running
if ! command -v python3 &> /dev/null; then
  echo "❌ Error: Python 3 is not installed! Aborting run."
  exit 1
fi

source venv/bin/activate
echo "🧪 Running unit tests with pytest..."
pytest || { echo "❌ Pytest suites failed! Aborting deployment."; exit 1; }

echo "⚙️ Running database migrations..."
flask db upgrade || python manage.py db upgrade || echo "⚠️ No migration scripts, continuing..."

echo "🚀 Starting Flask app with Gunicorn..."
gunicorn --workers 3 --bind 0.0.0.0:8000 --pid "$CICD_PID_FILE" app:app
📄 .cicd/health.sh bash
#!/bin/bash
set -e
curl -sf http://localhost:8000/health || exit 1
📂 GitHub Path: docs/examples/go-service/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing Go environment..."

# 1. Install Go if missing
if ! command -v go &> /dev/null; then
  echo "📥 Go is missing. Installing Go compiler..."
  if command -v apt-get &> /dev/null; then
    sudo apt-get update -y
    sudo apt-get install -y golang
  elif command -v yum &> /dev/null; then
    sudo yum update -y
    sudo yum install -y golang
  fi
fi

echo "🌸 Fetching Go dependency modules..."
go mod download
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# Ensure dependencies exist before running
if ! command -v go &> /dev/null; then
  echo "❌ Error: Go is not installed! Aborting run."
  exit 1
fi

echo "🧪 Running package tests..."
go test ./... || { echo "❌ Go test suites failed! Aborting deployment."; exit 1; }

echo "🔨 Compiling binary..."
go build -ldflags="-s -w" -o app ./cmd/server/main.go || go build -ldflags="-s -w" -o app main.go

echo "🚀 Starting Go binary..."
./app > server.log 2>&1 &
echo $! > "$CICD_PID_FILE"
📄 .cicd/health.sh bash
#!/bin/bash
set -e
curl -sf http://localhost:8080/health || exit 1
📂 GitHub Path: docs/examples/php-laravel/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing environment for PHP & Laravel..."

# 1. Install PHP and extensions if missing
if ! command -v php &> /dev/null; then
  echo "📥 Installing PHP & common extensions..."
  if command -v apt-get &> /dev/null; then
    sudo apt-get update -y
    sudo apt-get install -y php php-cli php-fpm php-mbstring php-xml php-bcmath php-curl php-zip php-mysql unzip
  elif command -v yum &> /dev/null; then
    sudo yum update -y
    sudo yum install -y php php-cli php-fpm php-mbstring php-xml php-bcmath php-curl php-zip php-mysqlnd unzip
  fi
fi

# 2. Install Composer if missing
if ! command -v composer &> /dev/null; then
  echo "📥 Installing Composer..."
  curl -sS https://getcomposer.org/installer | php
  sudo mv composer.phar /usr/local/bin/composer
fi

# 3. Install NPM/Node if missing
if ! command -v npm &> /dev/null; then
  echo "📥 Installing Node.js & NPM..."
  if command -v apt-get &> /dev/null; then
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt-get install -y nodejs
  elif command -v yum &> /dev/null; then
    curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
    sudo yum install -y nodejs
  fi
fi

# 4. Install Composer dependencies
echo "🌸 Installing Composer dependencies..."
composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev

# 5. Install front-end dependencies if package.json exists
if [ -f "package.json" ]; then
  echo "📦 Installing npm packages..."
  npm ci
fi
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# 1. Run database migrations
echo "⚙️ Running database migrations..."
php artisan migrate --force || echo "⚠️ Migration failed or not configured, continuing..."

# 2. Optimize Laravel cache
echo "⚡ Optimizing application configuration and routes..."
php artisan config:cache
php artisan route:cache
php artisan view:cache

# 3. Build front-end assets
if [ -f "package.json" ]; then
  echo "🔨 Compiling front-end assets..."
  npm run build
fi

# 4. Start local development server (background)
echo "🚀 Starting Laravel server..."
php artisan serve --host 0.0.0.0 --port 8000 > laravel.log 2>&1 &
echo $! > "$CICD_PID_FILE"
📄 .cicd/health.sh bash
#!/bin/bash
set -e
echo "🔍 Checking Laravel service health..."
curl -sf http://localhost:8000/ || exit 1
📂 GitHub Path: docs/examples/rust/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
if ! command -v cargo &> /dev/null; then
  echo "🦀 Installing Rust & Cargo toolchain..."
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
  source "$HOME/.cargo/env"
else
  echo "🦀 Cargo detected: $(cargo --version)"
fi
echo "📦 Fetching Cargo crates..."
cargo fetch
📄 .cicd/run.sh bash
#!/bin/bash
set -e
if [ -f "$HOME/.cargo/env" ]; then
  source "$HOME/.cargo/env"
fi
echo "🧪 Running unit & integration tests..."
cargo test || { echo "❌ Rust test suite failed! Aborting deployment."; exit 1; }

echo "🔨 Building release binary..."
cargo build --release

echo "🚀 Booting Rust service..."
./target/release/rust-service > rust.log 2>&1 &
echo $! > "$CICD_PID_FILE"
📄 .cicd/health.sh bash
#!/bin/bash
set -e
echo "🔍 Checking Rust service health..."
curl -sf http://localhost:8080/health || curl -sf http://localhost:8080/ || exit 1
📂 GitHub Path: docs/examples/react-static/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing React environment..."

# 1. Install Node.js/NPM if missing
if ! command -v node &> /dev/null; then
  echo "📥 Node.js is missing. Installing Node.js & NPM..."
  if command -v apt-get &> /dev/null; then
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt-get install -y nodejs
  elif command -v yum &> /dev/null; then
    curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
    sudo yum install -y nodejs
  fi
fi

echo "🌸 Installing React frontend packages..."
npm ci
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# Ensure dependencies exist before running
if ! command -v node &> /dev/null; then
  echo "❌ Error: Node.js is not installed! Aborting run."
  exit 1
fi

echo "🧪 Running frontend tests..."
npm test -- --watchAll=false || { echo "❌ React test suites failed! Aborting deployment."; exit 1; }

echo "🔨 Compiling React build folder..."
npm run build

echo "🚀 Starting background static server..."
npx serve -s build -l 5000 > static.log 2>&1 &
echo $! > "$CICD_PID_FILE"
📄 .cicd/health.sh bash
#!/bin/bash
set -e
curl -sf http://localhost:5000/ || exit 1
📂 GitHub Path: docs/examples/docker-compose/
📄 .cicd/install.sh bash
#!/bin/bash
set -e
echo "🌸 Initializing Docker environment..."

# 1. Install Docker & Compose if missing
if ! command -v docker &> /dev/null; then
  echo "📥 Docker is missing. Installing Docker & Docker Compose..."
  if command -v apt-get &> /dev/null; then
    sudo apt-get update -y
    sudo apt-get install -y docker.io docker-compose-v2
    sudo systemctl enable --now docker
  elif command -v yum &> /dev/null; then
    sudo yum update -y
    sudo yum install -y docker docker-compose-plugin
    sudo systemctl enable --now docker
  fi
fi

echo "🌸 Compiling and building docker images..."
docker compose build
📄 .cicd/run.sh bash
#!/bin/bash
set -e

# Ensure dependencies exist before running
if ! command -v docker &> /dev/null; then
  echo "❌ Error: Docker is not installed! Aborting run."
  exit 1
fi

echo "🚀 Starting Docker Compose containers..."
docker compose up -d --force-recreate

echo "🧪 Running tests inside app container..."
docker compose exec -T app npm test || { 
  echo "❌ Tests failed inside docker! Cleaning up and aborting.";
  docker compose down;
  exit 1; 
}

echo "⚙️ Initializing health monitor loop..."
(
  while true; do
    if [ "$(docker inspect -f '{{.State.Running}}' "$(docker compose ps -q app)")" != "true" ]; then
      echo "❌ App container crashed!"
      exit 1
    fi
    sleep 30
  done
) > compose-monitor.log 2>&1 &

echo $! > "$CICD_PID_FILE"
echo "✅ Docker Compose deployment initialized."
📄 .cicd/health.sh bash
#!/bin/bash
set -e
curl -sf http://localhost:8080/health || exit 1

Process Execution Tiers

CICD automatically monitors application execution using four monitoring Tiers:

Tier 1 — Foreground (Default)

Your application starts in the foreground (e.g. node index.js). The Go engine tracks the runner bash process directly. Recommended for most web applications.

Tier 2 — Background

For processes started in the background using the ampersand (e.g. node index.js &). CICD scans the active Linux process tree to monitor background children.

Tier 3 — Delegating (PM2 / Tmux / Screen)

If you use external managers, write the target PID to the environment variable $CICD_PID_FILE. The runner will monitor that specific PID.

# Example: run.sh using PM2
#!/bin/bash
pm2 delete "$CICD_SERVICE_NAME" 2>/dev/null || true
pm2 start index.js --name "$CICD_SERVICE_NAME"

# Write managed PID for telemetry
pm2 pid "$CICD_SERVICE_NAME" > "$CICD_PID_FILE"

Tier 4 — External (Docker / Daemons)

If your application operates as a Docker container or detached system daemon, track health using a custom health.sh file. CICD runs it in a loop every 30 seconds.

health.sh — Custom Health Watchdog

An optional script that checks system readiness. Exit code 0 indicates a healthy service. Any other exit code signals a crash, triggering automatic rolling restarts.

# Example: .cicd/health.sh
#!/bin/bash
curl -sf http://localhost:8080/health || exit 1
← Quick Start Next: Architecture →