Environment Management
This guide shows you how to manage Probe workflows across multiple environments (development, staging, production) using configuration composition, environment-specific settings, and deployment strategies.
Basic Environment Configuration
The goal is one workflow file that runs against development, staging and production, with only the values changing.
Single Workflow, Multiple Environments
Create a base workflow that works across environments:
base-workflow.yml:
name: Multi-Environment API Test
description: API testing workflow that adapts to different environments
vars:
api_base_url: "{{API_BASE_URL}}"
environment: "{{ENVIRONMENT ?? 'Unknown'}}"
default_timeout: "{{DEFAULT_TIMEOUT ?? '30s'}}"
jobs:
- id: api-health-check
name: API Health Check
defaults:
http:
headers:
User-Agent: "Probe Test Agent"
Accept: "application/json"
steps:
- name: Health Endpoint Test
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health"
test: res.code == 200
outputs:
api_healthy: res.code == 200
response_time: (rt.sec * 1000)
api_version: res.body.version
- name: Database Health Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/database"
test: res.code == 200
outputs:
database_healthy: res.code == 200
db_response_time: (rt.sec * 1000)
- id: environment-report
name: Environment Report
needs: [api-health-check]
defaults:
http:
headers:
User-Agent: "Probe Test Agent"
Accept: "application/json"
steps:
- name: Environment Summary
uses: hello
echo: |
🌍 Environment Test Report
=========================
Environment: {{vars.environment}}
API Base URL: {{vars.api_base_url}}
Health Check Results:
API Health: {{outputs.api_healthy ? "✅ Healthy" : "❌ Down"}} ({{outputs.response_time}}ms)
Database: {{outputs.database_healthy ? "✅ Healthy" : "❌ Down"}} ({{outputs.db_response_time}}ms)
API Version: {{outputs.api_version}}
Environment-Specific Notes:
{{vars.environment == "development" ? "• Development environment - extended timeouts enabled" : ""}}
{{vars.environment == "staging" ? "• Staging environment - production-like testing" : ""}}
{{vars.environment == "production" ? "• Production environment - strict validation" : ""}}
- id: dev-specific-checks
name: Development Environment Checks
needs: [api-health-check]
skipif: vars.environment != "development"
steps:
- name: Hot Reload Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/dev/hot-reload-status"
test: res.code == 200
outputs:
hot_reload_enabled: res.body.enabled
- name: Debug Endpoints Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/debug/info"
test: res.code == 200
outputs:
debug_info_available: res.code == 200
- name: Development Summary
uses: hello
echo: |
🛠️ Development Environment Status:
Hot Reload: {{outputs.hot_reload_enabled ? "✅ Enabled" : "❌ Disabled"}}
Debug Info: {{outputs.debug_info_available ? "✅ Available" : "❌ Not Available"}}
- id: staging-specific-checks
name: Staging Environment Checks
needs: [api-health-check]
skipif: vars.environment != "staging"
defaults:
http:
headers:
X-Environment: staging
steps:
- name: Load Balancer Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/load-balancer"
test: res.code == 200
outputs:
load_balancer_healthy: res.code == 200
backend_count: res.body.active_backends
- name: Cache Layer Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/cache"
test: res.code == 200
outputs:
cache_healthy: res.code == 200
cache_hit_rate: res.body.hit_rate
- name: Integration Tests
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/test/integration"
test: res.code == 200 && res.body.all_tests_passed == true
outputs:
integration_tests_passed: res.body.all_tests_passed
- name: Staging Summary
uses: hello
echo: |
🧪 Staging Environment Status:
Load Balancer: {{outputs.load_balancer_healthy ? "✅ Healthy" : "❌ Issues"}} ({{outputs.backend_count}} backends)
Cache Layer: {{outputs.cache_healthy ? "✅ Healthy" : "❌ Issues"}} ({{(outputs.cache_hit_rate * 100)}}% hit rate)
Integration Tests: {{outputs.integration_tests_passed ? "✅ Passed" : "❌ Failed"}}
- id: production-specific-checks
name: Production Environment Checks
needs: [api-health-check]
skipif: vars.environment != "production"
defaults:
http:
headers:
X-Environment: production
steps:
- name: SSL Certificate Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/ssl"
test: |
res.code == 200 &&
res.body.certificate_valid == true &&
res.body.days_until_expiry > 30
outputs:
ssl_valid: res.body.certificate_valid
ssl_days_remaining: res.body.days_until_expiry
- name: Performance SLA Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/performance"
test: |
res.code == 200 &&
res.body.avg_response_time < 500 &&
res.body.success_rate > 0.999
outputs:
sla_met: res.body.avg_response_time < 500 && res.body.success_rate > 0.999
avg_response_time: res.body.avg_response_time
success_rate: res.body.success_rate
- name: Security Compliance Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/security"
test: |
res.code == 200 &&
res.body.security_score >= 0.95
outputs:
security_compliant: res.body.security_score >= 0.95
security_score: res.body.security_score
- name: Production Summary
uses: hello
echo: |
🏭 Production Environment Status:
SSL Certificate: {{outputs.ssl_valid ? "✅ Valid" : "❌ Invalid"}} ({{outputs.ssl_days_remaining}} days remaining)
Performance SLA: {{outputs.sla_met ? "✅ Met" : "❌ Violated"}}
- Avg Response: {{outputs.avg_response_time}}ms
- Success Rate: {{(outputs.success_rate * 100)}}%
Security Compliance: {{outputs.security_compliant ? "✅ Compliant" : "❌ Non-Compliant"}} ({{(outputs.security_score * 100)}}%)development.yml:
vars:
environment: "{{ENVIRONMENT ?? 'development'}}"
api_base_url: "{{API_BASE_URL ?? 'http://localhost:3000'}}"
default_timeout: "{{DEFAULT_TIMEOUT ?? '60s'}}"staging.yml:
vars:
environment: "{{ENVIRONMENT ?? 'staging'}}"
api_base_url: "{{API_BASE_URL ?? 'https://api.staging.yourcompany.com'}}"
default_timeout: "{{DEFAULT_TIMEOUT ?? '30s'}}"production.yml:
vars:
environment: "{{ENVIRONMENT ?? 'production'}}"
api_base_url: "{{API_BASE_URL ?? 'https://api.yourcompany.com'}}"
default_timeout: "{{DEFAULT_TIMEOUT ?? '10s'}}"Usage:
# Development environment
probe base-workflow.yml,development.yml
# Staging environment
probe base-workflow.yml,staging.yml
# Production environment
probe base-workflow.yml,production.ymlThe environment files hold only vars. A top-level key in a later file replaces the one in an earlier file, so jobs in an environment file would replace every job of the base workflow, and its needs would name jobs that are no longer there; Probe warns of a file whose jobs replace those of an earlier one. The jobs for one environment are kept in the base workflow instead, and skipif skips them in the others.
When the environments differ only in a few values, the workflow can stay one file, and the values be given as it is run. --env-file reads the variables the workflow reads into its vars from a file, where the environment has none, and --var gives a var in place of what the workflow writes for it:
# The variables of an environment, kept in a file of NAME=value lines
probe --env-file staging.env workflow.yml
# One value changed for one run
probe --env-file staging.env --var api_url=http://localhost:8080 workflow.ymlSee --var and --env-file.
Advanced Environment Management
Environments differ in more than their addresses: which features are enabled, where the credentials come from, and whether the environment is configured correctly at all.
Environment-Specific Feature Flags
Control which features are tested in different environments. The file is a workflow of its own, with its own jobs, so it is run apart from the base workflow rather than merged into it:
feature-flags.yml:
name: Feature Flag Tests
# Feature flags configuration
vars:
api_base_url: "{{API_BASE_URL}}"
admin_token: "{{ADMIN_TOKEN}}"
environment: "{{ENVIRONMENT}}"
# Core features (always enabled)
feature_user_management: true
feature_basic_api: true
# Environment-specific features (a template gives a string, "true" or "false")
feature_beta_api: "{{vars.environment != 'production'}}"
feature_admin_tools: "{{vars.environment == 'development'}}"
feature_performance_testing: "{{vars.environment != 'development'}}"
feature_security_scanning: "{{vars.environment == 'production'}}"
jobs:
- id: core-feature-tests
name: Core Feature Tests
steps:
- name: User Management Test
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/users"
test: res.code == 200
outputs:
user_management_working: res.code == 200
- name: Basic API Test
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/api/basic"
test: res.code == 200
outputs:
basic_api_working: res.code == 200
- id: beta-feature-tests
name: Beta Feature Tests
skipif: vars.feature_beta_api != "true"
steps:
- name: Beta API Test
id: beta-feature-tests
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/api/beta"
test: res.code == 200
outputs:
beta_api_working: res.code == 200
- id: admin-feature-tests
name: Admin Feature Tests
skipif: vars.feature_admin_tools != "true"
steps:
- name: Admin Tools Test
id: admin-feature-tests
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/admin/tools"
headers:
Authorization: "Bearer {{vars.admin_token}}"
test: res.code == 200
outputs:
admin_tools_working: res.code == 200
- id: performance-tests
name: Performance Tests
skipif: vars.feature_performance_testing != "true"
steps:
- name: Load Test
id: performance-tests
uses: http
with:
url: "{{vars.api_base_url}}/test/load"
method: POST
body: |
{
"concurrent_users": {{vars.environment == "staging" ? 10 : 50}},
"duration_seconds": {{vars.environment == "staging" ? 60 : 300}}
}
test: res.code == 200
outputs:
load_test_passed: res.body.success
- id: security-tests
name: Security Tests
skipif: vars.feature_security_scanning != "true"
steps:
- name: Security Scan
id: security-tests
uses: http
with:
url: "{{vars.api_base_url}}/security/scan"
method: POST
test: res.code == 200 && res.body.vulnerabilities_found == 0
outputs:
security_scan_clean: res.body.vulnerabilities_found == 0
- id: feature-summary
name: Feature Test Summary
needs: [core-feature-tests, beta-feature-tests, admin-feature-tests, performance-tests, security-tests]
steps:
- name: Environment Feature Report
uses: hello
echo: |
🚀 Feature Test Summary for {{vars.environment}}:
================================================
CORE FEATURES:
{{vars.feature_user_management == true ? "User Management: " + (outputs.user_management_working ? "✅ Working" : "❌ Failed") : "User Management: ⏸️ Disabled"}}
{{vars.feature_basic_api == true ? "Basic API: " + (outputs.basic_api_working ? "✅ Working" : "❌ Failed") : "Basic API: ⏸️ Disabled"}}
BETA FEATURES:
{{vars.feature_beta_api == "true" ? "Beta API: " + (outputs['beta-feature-tests'].beta_api_working ? "✅ Working" : "❌ Failed") : "Beta API: ⏸️ Disabled"}}
ADMIN FEATURES:
{{vars.feature_admin_tools == "true" ? "Admin Tools: " + (outputs['admin-feature-tests'].admin_tools_working ? "✅ Working" : "❌ Failed") : "Admin Tools: ⏸️ Disabled"}}
PERFORMANCE TESTING:
{{vars.feature_performance_testing == "true" ? "Load Testing: " + (outputs['performance-tests'].load_test_passed ? "✅ Passed" : "❌ Failed") : "Performance Testing: ⏸️ Disabled"}}
SECURITY TESTING:
{{vars.feature_security_scanning == "true" ? "Security Scan: " + (outputs['security-tests'].security_scan_clean ? "✅ Clean" : "❌ Vulnerabilities Found") : "Security Testing: ⏸️ Disabled"}}
Environment Configuration:
Features enabled: {{
(vars.feature_user_management == true ? 1 : 0) +
(vars.feature_basic_api == true ? 1 : 0) +
(vars.feature_beta_api == "true" ? 1 : 0) +
(vars.feature_admin_tools == "true" ? 1 : 0) +
(vars.feature_performance_testing == "true" ? 1 : 0) +
(vars.feature_security_scanning == "true" ? 1 : 0)
}} / 6Credential and Secret Management
Credentials are not written in the YAML, and are not kept in a YAML file of their own either: given as one more file on the command line, its vars would replace those of the environment file. A workflow reads them from environment variables in its vars, and lists them under secrets, so that their values are hidden in the output and the reports:
secrets:
- API_TOKEN
vars:
api_token: "{{API_TOKEN}}"On a development machine, the values can be kept in a file of NAME=value lines that is not committed, and given with --env-file:
development.env:
ENVIRONMENT=development
API_BASE_URL=http://localhost:3000
API_TOKEN=dev_token_12345
ADMIN_TOKEN=dev_admin_tokenprobe --env-file development.env environment-validation.ymlIn CI, the environment of the job gives them from the secrets of the CI, as in the GitHub Actions example below.
Environment Validation Workflows
Validate environment configuration before running tests:
environment-validation.yml:
name: Environment Validation
description: Validate environment configuration and prerequisites
vars:
environment: "{{ENVIRONMENT ?? ''}}"
api_base_url: "{{API_BASE_URL ?? ''}}"
api_token: "{{API_TOKEN ?? ''}}"
log_level: "{{LOG_LEVEL ?? ''}}"
default_timeout: "{{DEFAULT_TIMEOUT ?? ''}}"
jobs:
- id: environment-validation
name: Environment Configuration Validation
steps:
- name: Required Environment Variables Check
uses: hello
echo: |
🔍 Environment Variables Validation:
Required Variables:
ENVIRONMENT: {{vars.environment != "" ? "✅ Set (" + vars.environment + ")" : "❌ Missing"}}
API_BASE_URL: {{vars.api_base_url != "" ? "✅ Set (" + vars.api_base_url + ")" : "❌ Missing"}}
API_TOKEN: {{vars.api_token != "" ? "✅ Set (***)" : "❌ Missing"}}
Optional Variables:
LOG_LEVEL: {{vars.log_level != "" ? "✅ Set (" + vars.log_level + ")" : "⚠️ Using default"}}
DEFAULT_TIMEOUT: {{vars.default_timeout != "" ? "✅ Set (" + vars.default_timeout + ")" : "⚠️ Using default"}}
Validation Status: {{
vars.environment != "" && vars.api_base_url != "" && vars.api_token != "" ? "✅ Valid" : "❌ Invalid"
}}
- name: Environment-Specific Validation
uses: hello
echo: |
📋 Environment-Specific Validation:
{{vars.environment == "development" ? "Development Environment:" : ""}}
{{vars.environment == "development" ? "• Extended timeouts enabled" : ""}}
{{vars.environment == "development" ? "• Debug features available" : ""}}
{{vars.environment == "development" ? "• Security checks relaxed" : ""}}
{{vars.environment == "staging" ? "Staging Environment:" : ""}}
{{vars.environment == "staging" ? "• Production-like configuration" : ""}}
{{vars.environment == "staging" ? "• Integration testing enabled" : ""}}
{{vars.environment == "staging" ? "• Performance testing included" : ""}}
{{vars.environment == "production" ? "Production Environment:" : ""}}
{{vars.environment == "production" ? "• Strict timeouts enforced" : ""}}
{{vars.environment == "production" ? "• Security scanning enabled" : ""}}
{{vars.environment == "production" ? "• Full monitoring active" : ""}}
- name: Service Connectivity Pre-Check
uses: http
timeout: 10s
with:
method: GET
url: "{{vars.api_base_url}}/health"
test: res.code == 200
outputs:
connectivity_ok: res.code == 200
api_version: res.body.version
environment_confirmed: res.body.environment
- name: Authentication Pre-Check
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/auth/validate"
headers:
Authorization: "Bearer {{vars.api_token}}"
test: res.code == 200
outputs:
auth_valid: res.code == 200
token_expires_in: res.body.expires_in
- name: Validation Summary
uses: hello
echo: |
✅ Environment Validation Results:
Connectivity: {{outputs.connectivity_ok ? "✅ Connected" : "❌ Failed"}}
API Version: {{outputs.api_version}}
Environment Match: {{outputs.environment_confirmed == vars.environment ? "✅ Confirmed" : "⚠️ Mismatch"}}
Authentication: {{outputs.auth_valid ? "✅ Valid" : "❌ Invalid"}}
{{outputs.auth_valid ? "Token Expires In: " + string(outputs.token_expires_in) + " seconds" : ""}}
Environment Ready: {{
outputs.connectivity_ok &&
outputs.auth_valid &&
outputs.environment_confirmed == vars.environment
? "🟢 YES" : "🔴 NO"
}}CI/CD Integration
CI already knows which environment a run targets. Passing that through lets the same workflow select its values and its test suite.
GitHub Actions Integration
Integrate with CI/CD pipelines for automated environment testing:
.github/workflows/probe-tests.yml:
name: Probe Environment Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
development-tests:
name: Development Environment Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Probe
run: |
curl -L https://github.com/mozership/probe/releases/latest/download/probe_linux_x86_64.tar.gz | tar -xz probe
chmod +x probe
sudo mv probe /usr/local/bin/
- name: Run Development Tests
run: |
probe workflows/base-workflow.yml,environments/development.yml
staging-tests:
name: Staging Environment Tests
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Install Probe
run: |
curl -L https://github.com/mozership/probe/releases/latest/download/probe_linux_x86_64.tar.gz | tar -xz probe
chmod +x probe
sudo mv probe /usr/local/bin/
- name: Validate Staging Environment
env:
ENVIRONMENT: staging
API_BASE_URL: https://api.staging.yourcompany.com
API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}
run: |
probe workflows/environment-validation.yml
- name: Run Staging Tests
run: |
probe workflows/base-workflow.yml,environments/staging.yml
- name: Run Feature Tests
env:
API_BASE_URL: https://api.staging.yourcompany.com
ADMIN_TOKEN: ${{ secrets.STAGING_ADMIN_TOKEN }}
run: |
probe --var environment=staging features/feature-flags.yml
production-smoke-tests:
name: Production Smoke Tests
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Install Probe
run: |
curl -L https://github.com/mozership/probe/releases/latest/download/probe_linux_x86_64.tar.gz | tar -xz probe
chmod +x probe
sudo mv probe /usr/local/bin/
- name: Production Smoke Tests
env:
API_BASE_URL: https://api.yourcompany.com
run: |
probe workflows/smoke-test.ymlEnvironment-Specific Test Suites
Create different test suites for different environments:
smoke-test.yml (for production):
name: Production Smoke Test
description: Minimal smoke test for production environment
vars:
api_base_url: "{{API_BASE_URL}}"
jobs:
- id: critical-endpoints
name: Critical Endpoints Check
steps:
- name: Health Check
id: api
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health"
test: res.code == 200
outputs:
api_healthy: res.code == 200
- name: Authentication Check
id: auth
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/auth/health"
test: res.code == 200
outputs:
auth_healthy: res.code == 200
- name: Database Check
id: database
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/database"
test: res.code == 200
outputs:
db_healthy: res.code == 200
- id: smoke-test-summary
name: Smoke Test Summary
needs: [critical-endpoints]
steps:
- name: Production Health Summary
uses: hello
echo: |
🏭 Production Smoke Test Results:
Critical Systems:
API: {{outputs.api.api_healthy ? "✅ Healthy" : "🚨 DOWN"}}
Authentication: {{outputs.auth.auth_healthy ? "✅ Healthy" : "🚨 DOWN"}}
Database: {{outputs.database.db_healthy ? "✅ Healthy" : "🚨 DOWN"}}
Overall Status: {{
outputs.api.api_healthy &&
outputs.auth.auth_healthy &&
outputs.database.db_healthy
? "🟢 ALL SYSTEMS OPERATIONAL" : "🔴 CRITICAL ISSUES DETECTED"
}}comprehensive-test.yml (for staging):
name: Comprehensive Staging Test
description: Full test suite for staging environment validation
vars:
api_base_url: "{{API_BASE_URL}}"
jobs:
- id: api-tests
name: API Test Suite
steps:
- name: User Management API
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/users"
test: res.code == 200
- name: Order Management API
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/orders"
test: res.code == 200
- name: Product Catalog API
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/products"
test: res.code == 200
- id: integration-tests
name: Integration Tests
needs: [api-tests]
steps:
- name: User-Order Integration
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/test/user-order-flow"
test: res.code == 200 && res.body.test_passed == true
- name: Payment Integration
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/test/payment-flow"
test: res.code == 200 && res.body.test_passed == true
- id: performance-tests
name: Performance Validation
needs: [integration-tests]
steps:
- name: Load Test
uses: http
with:
url: "{{vars.api_base_url}}/test/load"
method: POST
body: |
{
"concurrent_users": 10,
"duration_seconds": 60
}
test: res.code == 200 && res.body.success_rate > 0.95Environment Monitoring and Alerting
The same environment configuration that drives the tests can drive the monitoring, so each environment is checked on its own terms.
Environment Health Monitoring
Monitor the health of each environment continuously:
environment-monitor.yml:
name: Environment Health Monitor
description: Continuous monitoring of environment health
vars:
api_base_url: "{{API_BASE_URL}}"
environment: "{{ENVIRONMENT}}"
smtp_host: "{{SMTP_HOST}}"
monitoring_interval: "{{MONITORING_INTERVAL ?? '300'}}" # 5 minutes
jobs:
- id: environment-health-check
name: Environment Health Check
steps:
- name: System Resources Check
id: resources
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/resources"
test: |
res.code == 200 &&
res.body.cpu_usage < 80 &&
res.body.memory_usage < 80 &&
res.body.disk_usage < 90
outputs:
resources_healthy: |
res.code == 200 &&
res.body.cpu_usage < 80 &&
res.body.memory_usage < 80 &&
res.body.disk_usage < 90
cpu_usage: res.body.cpu_usage
memory_usage: res.body.memory_usage
disk_usage: res.body.disk_usage
- name: Service Dependencies Check
id: dependencies
uses: http
with:
method: GET
url: "{{vars.api_base_url}}/health/dependencies"
test: |
res.code == 200 &&
res.body.all_dependencies_healthy == true
outputs:
dependencies_healthy: res.body.all_dependencies_healthy
unhealthy_services: res.body.unhealthy_services
- name: Environment-Specific Checks
id: env-checks
uses: hello
echo: |
Environment-specific validation for {{vars.environment}}
outputs:
env_specific_checks: |
vars.environment == "production" ? "SSL, Security, Performance" :
vars.environment == "staging" ? "Integration, Load Testing" :
"Development Tools, Debug Features"
- id: alerting
name: Environment Alerting
needs: [environment-health-check]
always: true
skipif: outputs.resources?.resources_healthy == true && outputs.dependencies?.dependencies_healthy == true
steps:
- name: Environment Alert
uses: smtp
with:
addr: "{{vars.smtp_host}}:587"
from: "environment-alerts@yourcompany.com"
to: "devops@yourcompany.com"
subject: "🚨 Environment Health Alert - {{vars.environment}}"
session: 1
message: 1
length: 500
test: res.code == 0
echo: |
ENVIRONMENT HEALTH ALERT
========================
Environment: {{vars.environment}}
Time: {{unixtime()}}
RESOURCE STATUS:
{{outputs.resources?.resources_healthy == true ? "✅ Resources Healthy" : "❌ Resource Issues"}}
CPU Usage: {{outputs.resources?.cpu_usage ?? "unknown"}}%
Memory Usage: {{outputs.resources?.memory_usage ?? "unknown"}}%
Disk Usage: {{outputs.resources?.disk_usage ?? "unknown"}}%
DEPENDENCIES STATUS:
{{outputs.dependencies?.dependencies_healthy == true ? "✅ All Dependencies Healthy" : "❌ Dependency Issues"}}
Unhealthy Services: {{join(outputs.dependencies?.unhealthy_services ?? [], ", ")}}
ACTION REQUIRED: Investigate {{vars.environment}} environment immediately
- id: health-summary
name: Health Summary
needs: [environment-health-check]
steps:
- name: All Systems Healthy
uses: hello
echo: |
✅ {{vars.environment}} Environment Health Check
All systems operational:
• Resources: CPU {{outputs.resources.cpu_usage}}%, Memory {{outputs.resources.memory_usage}}%, Disk {{outputs.resources.disk_usage}}%
• Dependencies: All healthy
• Environment: {{vars.environment}}
Next check in {{vars.monitoring_interval}} secondsBest Practices
The points below cover keeping environments apart, moving changes through them in order, validating the configuration, and handling secrets.
1. Environment Isolation
One file per environment keeps the values apart and makes the differences visible in a diff.
# Good: Clear environment separation
environments/
├── development.yml
├── staging.yml
└── production.yml
# Good: Credentials in the environment, not in YAML
development.env (not committed)
CI secrets → the environment of the job2. Progressive Testing
Each environment carries the checks that make sense there, and the heavier ones run before production.
# Good: Test pipeline progression
Development → Unit Tests
Staging → Integration Tests + Performance Tests
Production → Smoke Tests + Monitoring3. Configuration Validation
A run against a half-configured environment fails in ways that look like application failures, so the configuration is checked first.
# Good: Validate before running tests
vars:
environment: "{{ENVIRONMENT ?? ''}}"
api_base_url: "{{API_BASE_URL ?? ''}}"
api_token: "{{API_TOKEN ?? ''}}"- name: Pre-Test Validation
uses: hello
echo: |
Environment: {{vars.environment != "" ? "✅" : "❌"}}
API URL: {{vars.api_base_url != "" ? "✅" : "❌"}}
Credentials: {{vars.api_token != "" ? "✅" : "❌"}}4. Secure Secret Management
Secrets are read from the environment, so the environment file can be committed while the values are not.
# Good: Use environment variables for secrets
vars:
api_token: "{{PROD_API_TOKEN}}" # From secure env var
# Avoid: Hardcoded secrets
vars:
api_token: "hardcoded_secret_123" # Never do thisList the variables in secrets as well, so that their values are replaced by <secret:NAME> in the terminal output, the action logs and the report files, where a CI log or an uploaded artifact would otherwise keep them:
secrets:
- PROD_API_TOKEN
vars:
api_token: "{{PROD_API_TOKEN}}"What’s Next?
Now that you can manage environments effectively, explore:
- Monitoring Workflows - Build comprehensive monitoring for all environments
- API Testing - Create environment-specific API test suites
- Error Handling Strategies - Handle environment-specific failures
Environment management is crucial for reliable testing across the software development lifecycle. Use these patterns to create consistent, secure, and maintainable environment configurations.