Перейти к основному содержимому

Variables Overview

Variables provide dynamic configuration values that can be reused throughout your Xec configuration. They enable flexible, DRY (Don't Repeat Yourself) configurations that adapt to different environments and contexts.

Variable Types

Simple Variables

vars:
# Strings
appName: myapp
environment: production

# Numbers
port: 8080
replicas: 3
timeout: 30000

# Booleans
debug: false
enableCache: true

Complex Variables

vars:
# Objects
database:
host: db.example.com
port: 5432
name: production_db
credentials:
username: dbuser
password: ${secrets.db_password}

# Arrays
servers:
- web1.example.com
- web2.example.com
- web3.example.com

# Mixed structures
config:
features:
- name: feature-a
enabled: true
- name: feature-b
enabled: false
settings:
maxConnections: 100
timeout: 30s

Variable Interpolation

Basic Interpolation

vars:
name: myapp
version: "1.2.3"
tag: "${name}:${version}" # Result: myapp:1.2.3

tasks:
deploy:
command: docker run ${tag}

Nested Variables

vars:
env: production
region: us-east-1
endpoint: "https://api.${region}.example.com/${env}"

database:
host: db.example.com
port: 5432
url: "postgres://${database.host}:${database.port}/mydb"

Array Access

vars:
servers:
- primary.example.com
- secondary.example.com

primaryServer: ${servers[0]}
backupServer: ${servers[1]}

Variable Sources

1. Configuration Variables

Defined in vars section:

vars:
appName: myapp
version: "2.0.0"

2. Environment Variables

Access system environment:

vars:
home: ${env.HOME}
user: ${env.USER}
customPath: ${env.CUSTOM_PATH}

# With defaults
apiUrl: ${env.API_URL:-http://localhost:3000}
logLevel: ${env.LOG_LEVEL:-info}

3. Secrets

Access secure values:

vars:
apiKey: ${secrets.api_key}
dbPassword: ${secrets.database_password}
sshKey: ${secrets.deploy_key}

4. Task Parameters

Access task parameters:

tasks:
deploy:
params:
- name: version
required: true
command: |
docker pull myapp:${params.version}
docker run myapp:${params.version}

5. Runtime Variables

Dynamic values during execution:

tasks:
info:
command: |
echo "Profile: ${profile}"
echo "Target: ${target.name}"
echo "Task: ${task.name}"
echo "Date: ${runtime.date}"

Variable Scope

Global Scope

Available everywhere:

vars:
globalVar: "available-everywhere"

tasks:
use-global:
command: echo ${globalVar}

profiles:
prod:
vars:
url: "https://${globalVar}.example.com"

Profile Scope

Override global variables:

vars:
environment: development

profiles:
production:
vars:
environment: production # Overrides global

tasks:
show-env:
command: echo ${environment} # Uses profile value

Task Scope

Task-specific variables:

tasks:
scoped:
vars:
taskVar: "only-in-this-task"
command: echo ${taskVar}

other:
command: echo ${taskVar} # Error: not defined

Step Scope

Step-level variables:

tasks:
multi-step:
steps:
- command: echo "test"
register: output

- command: echo "Result: ${output.stdout}"
# output only available after registration

Default Values

Using Defaults

vars:
# With pipe operator
port: ${env.PORT:-8080}

# Nested defaults
database:
host: ${env.DB_HOST:-localhost}
port: ${env.DB_PORT:-5432}
name: ${env.DB_NAME:-development}

Conditional Defaults

vars:
environment: ${env.ENV:-development}

# Different defaults per environment
apiUrl: |
${environment == 'production'
? 'https://api.example.com'
: 'http://localhost:3000'}

Variable Functions

String Functions

vars:
name: "My App"

# String manipulation
lower: ${name.toLowerCase()} # my app
upper: ${name.toUpperCase()} # MY APP
slug: ${name.replace(' ', '-')} # My-App

Array Functions

vars:
servers:
- web1
- web2
- web3

serverCount: ${servers.length} # 3
firstServer: ${servers[0]} # web1
lastServer: ${servers[-1]} # web3
serverList: ${servers.join(',')} # web1,web2,web3

Object Functions

vars:
config:
host: localhost
port: 8080

configKeys: ${Object.keys(config)} # ['host', 'port']
configValues: ${Object.values(config)} # ['localhost', 8080]

Computed Variables

Simple Computation

vars:
base: 8080
offset: 100
port: ${base + offset} # 8180

replicas: 3
totalReplicas: ${replicas * 2} # 6

Complex Computation

vars:
environment: production
region: us-east-1

# Computed URL
apiEndpoint: |
https://api-${environment}.${region}.example.com

# Conditional computation
replicas: |
${environment === 'production' ? 5 : 1}

Variable Validation

Type Validation

vars:
port: ${env.PORT} # Must be number

tasks:
validate:
script: |
if (typeof port !== 'number') {
throw new Error('Port must be a number');
}

Required Variables

vars:
required: ${env.REQUIRED_VAR} # Fails if not set
optional: ${env.OPTIONAL_VAR:-default} # Has default

Variable Resolution Order

Variables are resolved in this precedence:

  1. Command-line - Highest priority
  2. Environment variables - XEC_VARS_*
  3. Profile variables - Active profile
  4. Task parameters - Task-specific
  5. Global variables - Config vars
  6. Defaults - Lowest priority

Advanced Patterns

Variable Templates

vars:
template:
url: "https://${service}.${environment}.example.com"

services:
api: ${template.url.replace('${service}', 'api')}
web: ${template.url.replace('${service}', 'web')}

Dynamic Variable Loading

tasks:
load-config:
script: |
const config = await loadExternalConfig();
xec.setVars({
dynamicVar: config.value,
computedVar: config.computed
});

Variable Inheritance

vars:
base:
timeout: 30000
retries: 3

extended:
$merge: [base]
timeout: 60000 # Override
newProp: value # Add new

Escaping Variables

Literal Dollar Signs

vars:
# Escape with backslash
literal: "Price: \$100"

# Or use single quotes
command: 'echo $HOME' # Not interpolated

Preventing Interpolation

tasks:
no-interpolation:
command: |
# Use $$ for literal $
echo "$$HOME"

# Or disable interpolation
$raw: true
command: echo $HOME ${var}

Best Practices

1. Use Descriptive Names

# Good
vars:
apiEndpoint: https://api.example.com
maxRetries: 3

# Bad
vars:
url: https://api.example.com
n: 3
vars:
database:
host: db.example.com
port: 5432
name: myapp

cache:
host: cache.example.com
port: 6379

3. Provide Defaults

vars:
# Always provide sensible defaults
port: ${env.PORT:-8080}
environment: ${env.NODE_ENV:-development}

4. Document Variables

vars:
# Maximum number of retry attempts for API calls
maxRetries: 3

# API endpoint URL (must include protocol)
apiUrl: https://api.example.com

5. Validate Early

tasks:
validate-config:
script: |
// Validate required variables
const required = ['apiKey', 'dbPassword'];
for (const key of required) {
if (!vars[key]) {
throw new Error(`Missing required variable: ${key}`);
}
}

Common Issues

Circular References

# This causes infinite loop
vars:
a: ${b}
b: ${a} # Error: circular reference

Undefined Variables

vars:
# This fails if MISSING is not defined
value: ${env.MISSING}

# Use default to prevent failure
value: ${env.MISSING:-default}

Type Mismatches

vars:
port: "8080" # String

tasks:
connect:
# May fail if expecting number
command: connect --port ${port}

# Better: ensure correct type
command: connect --port ${parseInt(port)}

Debugging Variables

Show Resolved Values

# Show all variables
xec config show --vars

# Show specific variable
xec config get vars.database.host

# Debug interpolation
xec --debug run task

Trace Resolution

tasks:
debug-vars:
script: |
console.log('All vars:', vars);
console.log('Environment:', env);
console.log('Profile:', profile);

Next Steps

See Also