The .cicd Folder
The .cicd/ directory in your repository root houses the execution hooks that CICD runs to install dependencies and boot your application.
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.
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:
#!/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
#!/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"
#!/bin/bash
set -e
curl -sf http://localhost:3000/health || exit 1
#!/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
#!/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
#!/bin/bash
set -e
curl -sf http://localhost:8000/health || exit 1
#!/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
#!/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"
#!/bin/bash
set -e
curl -sf http://localhost:8080/health || exit 1
#!/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
#!/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"
#!/bin/bash
set -e
echo "🔍 Checking Laravel service health..."
curl -sf http://localhost:8000/ || exit 1
#!/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
#!/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"
#!/bin/bash
set -e
echo "🔍 Checking Rust service health..."
curl -sf http://localhost:8080/health || curl -sf http://localhost:8080/ || exit 1
#!/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
#!/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"
#!/bin/bash
set -e
curl -sf http://localhost:5000/ || exit 1
#!/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
#!/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."
#!/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