Script Payloads
Script payloads run a script file that lives in a configured storage backend, enabling complex deployment logic with conditionals, loops, and error handling.
A script payload is not a script body you type into a text box. Instead, it references a file by name (and optional relative path) inside a storage backend you have configured. Mantis tells the target to execute that file with the chosen executor.
When to Use Scripts
Section titled “When to Use Scripts”| Use Scripts For | Use Commands Instead |
|---|---|
| Multi-step workflows | Single operations |
| Conditional logic | Simple tool invocations |
| Error handling | Atomic commands |
| Loops and iteration | Quick system commands |
| Variable manipulation | One-liners |
How a Script Payload Is Defined
Section titled “How a Script Payload Is Defined”A script payload is made up of:
| Field | Description |
|---|---|
| Storage backend | The configured storage where the script file is located. |
| File name | The name of the script file to execute. |
| Relative path | Optional path within the storage backend, if the file lives in a subfolder. |
| Executor | How the script is run (see Executor Reference). |
| Arguments | Optional arguments passed to the script. |
To create a script payload:
- Select a configured storage backend.
- Reference the script file by its file name (and relative path if it is in a subfolder).
- Choose an executor.
- Optionally supply arguments.
The script file itself is authored and stored in your storage backend, not edited inline in Mantis.
Example Script Files
Section titled “Example Script Files”The following are examples of script files you would place in your storage backend and then reference from a script payload.
Bash Scripts
Section titled “Bash Scripts”#!/bin/bashset -e # Exit on error
# Your deployment logicecho "Starting deployment..."cd /opt/myapp
# Pull latest codegit pull origin main
# Restart servicesystemctl restart myapp
echo "Deployment complete!"PowerShell Scripts
Section titled “PowerShell Scripts”$ErrorActionPreference = "Stop"
Write-Host "Starting deployment..."
# Your deployment logicSet-Location C:\Apps\MyApp
# Copy new filesCopy-Item -Path "\\server\share\release\*" -Destination "." -Recurse -Force
# Restart serviceRestart-Service -Name "MyAppService"
Write-Host "Deployment complete!"Python Scripts
Section titled “Python Scripts”#!/usr/bin/env python3import subprocessimport sys
def main(): print("Starting deployment...")
# Your deployment logic result = subprocess.run(["docker", "compose", "up", "-d"], check=True)
print("Deployment complete!") return 0
if __name__ == "__main__": sys.exit(main())Executor Reference
Section titled “Executor Reference”| Executor | Shebang | Error Handling |
|---|---|---|
| Default | N/A (direct exec) | Process exit code |
| Sh | #!/bin/sh | set -e |
| Bash | #!/bin/bash | set -e |
| Pwsh | #!/usr/bin/env pwsh | $ErrorActionPreference = "Stop" |
| Zsh | #!/bin/zsh | set -e |
Zsh is invoked with -f, so no per-user startup file (~/.zshenv, ~/.zshrc, …) is
sourced; anything the script needs from the environment has to come from the action’s
own environment variables.
Variables and Templates
Section titled “Variables and Templates”Mantis gives a script its runtime values in two ways: templates it renders before the script
runs, and MANTIS_* environment variables the script reads while it runs.
Templates
Section titled “Templates”The file name, arguments, working directory, script body and declared
environment variable values are templates. Mantis renders every #{...} in them before the
script runs:
| Template | Example value | Result |
|---|---|---|
#{version} | 1.2.3 | 1.2.3 |
#{environment | ToLower} | Staging | staging |
#{if Live}--live#{else}--dry-run#{/if} | true | --live |
#{Mantis.Environment.Name} | (system variable) | Production |
An argument of --version=#{version} becomes --version=1.2.3, and a file name of
deploy-#{environment}.sh resolves to deploy-staging.sh. See
Variables for the filters, conditionals and Mantis.* system
variables a template can use.
Only #{...} is a template, so a shell script’s own ${HOME} or ${1:-default} reaches
the shell exactly as written. Write ##{ for a literal #{ — for example a Ruby
interpolation puts "##{name}", which the script receives as puts "#{name}".
A template that does not render — an unterminated #{, an unknown filter, a #{calc} that
divides by zero — fails the step with a message naming the field, rather than running the
script half-rendered.
Sensitive Variables
Section titled “Sensitive Variables”Reference a sensitive variable bare, as #{DbPassword}. Mantis fills its value in when the
command is delivered to the target, so it is never stored in plaintext with the queued
command, and only the sensitive variables a script references are delivered to it. A
sensitive variable cannot take a filter or appear in a condition: #{DbPassword | ToUpper}
fails the step, because the value is not available where the filter would run.
A sensitive variable is not in the MANTIS_* set. To read one from the environment, declare
an environment variable on the action whose value references it, such as DB_PASSWORD =
#{DbPassword}.
Values at Run Time: MANTIS_* Environment Variables
Section titled “Values at Run Time: MANTIS_* Environment Variables”Mantis also merges resolved non-sensitive variables into the execution environment as
MANTIS_* environment variables (for example, MANTIS_VERSION, MANTIS_ENVIRONMENT). Your script
reads these the normal way for its shell.
Bash:
#!/bin/bashset -e
echo "Deploying version $MANTIS_VERSION to $MANTIS_ENVIRONMENT"
docker pull myregistry/myapp:"$MANTIS_VERSION"docker tag myregistry/myapp:"$MANTIS_VERSION" myregistry/myapp:currentPowerShell:
$ErrorActionPreference = "Stop"
Write-Host "Deploying version $env:MANTIS_VERSION to $env:MANTIS_ENVIRONMENT"Python:
import osversion = os.environ.get("MANTIS_VERSION")print(f"Deploying version {version}")Environment Variables
Section titled “Environment Variables”Setting in Mantis
Section titled “Setting in Mantis”Define environment variables on the action:
| Name | Value | Sensitive |
|---|---|---|
DB_HOST | db.example.com | No |
DB_PASSWORD | *** | Yes |
These are in addition to the MANTIS_* variables Mantis injects for resolved deployment variables.
Accessing in Scripts
Section titled “Accessing in Scripts”Bash:
#!/bin/bashecho "Connecting to $DB_HOST"psql -h "$DB_HOST" -U admin -c "SELECT 1"PowerShell:
Write-Host "Connecting to $env:DB_HOST"Invoke-Sqlcmd -ServerInstance $env:DB_HOST -Query "SELECT 1"Python:
import osdb_host = os.environ.get("DB_HOST")print(f"Connecting to {db_host}")Error Handling
Section titled “Error Handling”#!/bin/bashset -e # Exit on any errorset -o pipefail # Exit on pipe failures
# Or handle errors explicitlyif ! docker pull myimage; then echo "Failed to pull image" exit 1fi
# Cleanup on errorcleanup() { echo "Cleaning up..." rm -f /tmp/deploy-*}trap cleanup EXITPowerShell
Section titled “PowerShell”$ErrorActionPreference = "Stop"
try { # Deployment logic Copy-Item -Path $source -Destination $dest Restart-Service -Name $serviceName}catch { Write-Error "Deployment failed: $_" exit 1}finally { # Cleanup Remove-Item -Path $tempFiles -ErrorAction SilentlyContinue}Python
Section titled “Python”#!/usr/bin/env python3import subprocessimport sys
try: subprocess.run(["docker", "compose", "up", "-d"], check=True)except subprocess.CalledProcessError as e: print(f"Deployment failed: {e}", file=sys.stderr) sys.exit(1)except Exception as e: print(f"Unexpected error: {e}", file=sys.stderr) sys.exit(1)Common Patterns
Section titled “Common Patterns”These scripts live in your storage backend. Runtime values are read from MANTIS_* environment variables.
Blue-Green Deployment
Section titled “Blue-Green Deployment”#!/bin/bashset -e
CURRENT=$(readlink /opt/app/current || echo "blue")NEW=$([ "$CURRENT" = "blue" ] && echo "green" || echo "blue")PORT=$([ "$NEW" = "blue" ] && echo 8080 || echo 8081)
echo "Current: $CURRENT, Deploying to: $NEW"
# Deploy to new slotcd /opt/app/$NEWdocker compose pulldocker compose up -d
# Health checkfor i in {1..30}; do if curl -sf "http://localhost:$PORT/health"; then echo "Health check passed" break fi sleep 2done
# Switch trafficln -sfn /opt/app/$NEW /opt/app/currentnginx -s reload
# Stop oldcd /opt/app/$CURRENTdocker compose downDatabase Migration
Section titled “Database Migration”#!/bin/bashset -e
BACKUP_FILE="/backups/pre-migration-$(date +%Y%m%d-%H%M%S).sql"
echo "Creating backup..."pg_dump "$DATABASE_URL" > "$BACKUP_FILE"
echo "Running migrations..."if ! alembic upgrade head; then echo "Migration failed, restoring backup..." psql "$DATABASE_URL" < "$BACKUP_FILE" exit 1fi
echo "Migrations complete!"Rolling Service Update
Section titled “Rolling Service Update”#!/bin/bashset -e
SERVICES=(web-1 web-2 web-3)VERSION="$MANTIS_VERSION"
for service in "${SERVICES[@]}"; do echo "Updating $service..."
# Remove from load balancer curl -X POST "http://lb/api/remove?host=$service"
# Update service ssh "$service" "docker pull myapp:$VERSION && docker compose up -d"
# Wait for health until curl -sf "http://$service/health"; do sleep 5 done
# Add back to load balancer curl -X POST "http://lb/api/add?host=$service"
echo "$service updated"done
echo "Rolling update complete!"Working with Files
Section titled “Working with Files”File Downloads
Section titled “File Downloads”#!/bin/bashset -e
ARTIFACT_URL="$MANTIS_ARTIFACT_URL"DEST_DIR="/opt/app"
# Download artifactcurl -fSL "$ARTIFACT_URL" -o /tmp/release.tar.gz
# Extracttar -xzf /tmp/release.tar.gz -C "$DEST_DIR"
# Cleanuprm /tmp/release.tar.gzConfiguration Templates
Section titled “Configuration Templates”#!/bin/bashset -e
# Generate config from template using injected MANTIS_* valuescat > /opt/app/config.yml << EOFserver: host: 0.0.0.0 port: ${MANTIS_PORT}database: host: ${DB_HOST} name: ${MANTIS_DB_NAME}logging: level: ${MANTIS_LOG_LEVEL}EOF
echo "Configuration written"Logging Best Practices
Section titled “Logging Best Practices”Structured Output
Section titled “Structured Output”#!/bin/bash
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"}
log "Starting deployment"log "Version: $MANTIS_VERSION"log "Environment: $MANTIS_ENVIRONMENT"
# ... deployment steps ...
log "Deployment complete"Progress Indicators
Section titled “Progress Indicators”#!/bin/bash
echo "Step 1/4: Pulling images..."docker pull myapp:"$MANTIS_VERSION"
echo "Step 2/4: Stopping services..."docker compose down
echo "Step 3/4: Starting services..."docker compose up -d
echo "Step 4/4: Running health checks..."./health-check.sh
echo "Deployment successful"Security Considerations
Section titled “Security Considerations”Never Echo Secrets
Section titled “Never Echo Secrets”Bad:
echo "Using password: $DB_PASSWORD"Good:
echo "Connecting to database..."# Password is in environment, not loggedUse Secure Temporary Files
Section titled “Use Secure Temporary Files”#!/bin/bashTEMP_DIR=$(mktemp -d)trap "rm -rf $TEMP_DIR" EXIT
# Use $TEMP_DIR for temporary filesValidate Inputs
Section titled “Validate Inputs”#!/bin/bashset -e
VERSION="$MANTIS_VERSION"
# Validate version formatif [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Invalid version format: $VERSION" exit 1fiPerformance Tips
Section titled “Performance Tips”- Minimize network calls - Batch operations where possible
- Use parallel execution - When operations are independent
- Cache dependencies - Avoid re-downloading unchanged files
- Clean up - Remove temporary files to free disk space
Troubleshooting
Section titled “Troubleshooting”Script Exits Immediately
Section titled “Script Exits Immediately”Cause: set -e with a failing command
Solution: Handle expected failures explicitly:
set -esome_command || true # Ignore failureVariables Not Expanded
Section titled “Variables Not Expanded”Cause: Using single quotes, which prevent the shell from expanding variables.
Fix:
# Wrongecho 'Version: $MANTIS_VERSION'
# Rightecho "Version: $MANTIS_VERSION"Template Left in the Script Unchanged
Section titled “Template Left in the Script Unchanged”Cause: The variable is not defined for the deployment, or its name differs in case.
Fix: Define the variable, or correct the name in #{name}.
Windows Line Endings
Section titled “Windows Line Endings”Cause: A script file created on Windows may use CRLF (\r\n) line endings.
Fix: Convert the file to Unix (LF) line endings before storing it in your storage backend.
Next Steps
Section titled “Next Steps”- Command Payloads - For simpler operations
- Testing Actions - Validate scripts before deployment
- Versioning - Managing script changes
