From 6bcbc21dac99675778bee19694746997ea43205a Mon Sep 17 00:00:00 2001
From: Lucian Behind The Scenes
Date: Sun, 9 Aug 2026 13:29:27 +0300
Subject: [PATCH 1/3] docs: remove deployerphp.com references and document docs
site usage
The deployerphp.com domain is being retired. Point readers at the docs
folder and the loadinglucian/deployerphp.com repo for a local docs site.
---
README.md | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index d51c972..4b40f74 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
@@ -10,10 +10,6 @@
-
- https://deployerphp.com
-
-
# Meet DeployerPHP
This is DeployerPHP, a complete set of CLI tools for provisioning, installing, and deploying servers and sites using PHP. It serves as an open-source alternative to services such as Ploi, RunCloud or Laravel Forge.
@@ -28,6 +24,7 @@ Here it is in action:
- [Crash Course](#crash-course)
+- [Documentation](#documentation)
- [Benefits](#benefits)
- [Unlimited Servers & Sites](#unlimited-servers--sites)
- [No Vendor Lock-In](#no-vendor-lock-in)
@@ -84,6 +81,14 @@ deployer site:deploy
deployer site:https
```
+
+
+## Documentation
+
+The full documentation lives in the [docs](/docs) folder of this repository.
+
+To browse it as a beautiful documentation website, clone the [deployerphp.com](https://github.com/loadinglucian/deployerphp.com) repository and run it locally — it renders the documentation from this package. See its README for setup instructions.
+
## Benefits
From a3c2d86d7b71a74d8b860ff312c6879e98d460d6 Mon Sep 17 00:00:00 2001
From: Lucian Behind The Scenes
Date: Sun, 9 Aug 2026 13:29:27 +0300
Subject: [PATCH 2/3] test: comment out cloud provider bats tests
The AWS, DigitalOcean, and Cloudflare test accounts and domains have
been spun down; these tests cannot run without live credentials.
---
tests/bats/cloud-aws.bats | 1811 +++++++++++++++++++------------------
tests/bats/cloud-do.bats | 1683 +++++++++++++++++-----------------
2 files changed, 1752 insertions(+), 1742 deletions(-)
diff --git a/tests/bats/cloud-aws.bats b/tests/bats/cloud-aws.bats
index 0d8fcdb..5fa7aac 100644
--- a/tests/bats/cloud-aws.bats
+++ b/tests/bats/cloud-aws.bats
@@ -1,906 +1,911 @@
#!/usr/bin/env bats
-# AWS Integration Tests
-# Tests: AWS key/provision/DNS + site lifecycle commands
+# NOTE: These tests are disabled because the cloud provider accounts
+# (AWS, DigitalOcean, Cloudflare) and test domains they depend on have
+# been spun down. Uncomment the lines below to re-enable them once
+# active credentials are available again.
#
-# Prerequisites:
-# - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION in environment
-# - Valid AWS credentials with EC2 permissions
-# - SSH private key at ~/.ssh/id_ed25519
-
-load 'lib/helpers'
-load 'lib/cloud-helpers'
-
-# ----
-# Setup/Teardown
-# ----
-
-setup_file() {
- require_aws_credentials
- require_cf_credentials
- require_aws_dns_prefix_config
- require_cf_dns_prefix_config
- cloud_note "Run suffix: ${BATS_RUN_SUFFIX}"
- aws_cleanup_all
- cf_cleanup_all
-}
-
-teardown_file() {
- if ! aws_credentials_available; then
- return 0
- fi
- aws_cleanup_all
- cf_cleanup_all
- rm -f "$TEST_INVENTORY"
-}
-
-setup() {
- cloud_check_failed
- require_aws_credentials
-}
-
-teardown() {
- cloud_mark_failed
-}
-
-# ----
-# aws:key:add
-# ----
-
-@test "aws:key:add uploads public key to AWS" {
- run_deployer aws:key:add \
- --name="$AWS_TEST_KEY_NAME" \
- --public-key-path="$CLOUD_TEST_KEY_PATH"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Key pair imported successfully"
- assert_output_contains "Name: $AWS_TEST_KEY_NAME"
- assert_command_replay "aws:key:add"
-}
-
-# ----
-# aws:key:list
-# ----
-
-@test "aws:key:list shows uploaded key" {
- run_deployer aws:key:list
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "$AWS_TEST_KEY_NAME"
- assert_command_replay "aws:key:list"
-}
-
-# ----
-# aws:key:delete
-# ----
-
-@test "aws:key:delete removes key from AWS" {
- run_deployer aws:key:delete \
- --key="$AWS_TEST_KEY_NAME" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Key pair deleted successfully"
- assert_command_replay "aws:key:delete"
-}
-
-@test "aws:key:list confirms key deleted" {
- run_deployer aws:key:list
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_not_contains "$AWS_TEST_KEY_NAME"
-}
-
-# ----
-# aws:provision
-# ----
-
-@test "aws:provision creates EC2 instance and adds to inventory" {
- require_aws_provision_config
-
- # Cleanup any leftover test server
- aws_cleanup_test_server
-
- run_deployer aws:provision \
- --name="$AWS_TEST_SERVER_NAME" \
- --instance-type="$AWS_TEST_INSTANCE_TYPE" \
- --image="$AWS_TEST_IMAGE" \
- --key-pair="$AWS_TEST_KEY_PAIR" \
- --private-key-path="$AWS_TEST_PRIVATE_KEY_PATH" \
- --vpc="$AWS_TEST_VPC" \
- --subnet="$AWS_TEST_SUBNET" \
- --disk-size="$AWS_TEST_DISK_SIZE" \
- --no-monitoring
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Instance provisioned"
- assert_output_contains "Instance is running"
- assert_output_contains "Elastic IP allocated"
- assert_output_contains "Server added to inventory"
- assert_command_replay "aws:provision"
-
- # Tag contract for janitor discovery
- aws_assert_instance_tag_contract "$AWS_TEST_SERVER_NAME"
- aws_assert_eip_tag_contract "$AWS_TEST_SERVER_NAME"
- aws_assert_root_volume_tag_contract "$AWS_TEST_SERVER_NAME"
-}
-
-@test "server:install configures AWS provisioned server" {
- require_aws_provision_config
-
- local primary_php_version secondary_php_version installed_php_versions
- primary_php_version="$CLOUD_TEST_PHP_PRIMARY_VERSION"
- secondary_php_version="$CLOUD_TEST_PHP_SECONDARY_VERSION"
-
- # Full install takes time - use longer timeout
- run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
- --server="$AWS_TEST_SERVER_NAME" \
- --generate-deploy-key \
- --timezone="UTC" \
- --php-version="$primary_php_version" \
- --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Server installation completed"
- assert_output_contains "public key"
- assert_command_replay "server:install"
-
- # Install secondary PHP-FPM version on the same server (keep primary as default)
- run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
- --server="$AWS_TEST_SERVER_NAME" \
- --generate-deploy-key \
- --timezone="UTC" \
- --php-version="$secondary_php_version" \
- --no-php-default \
- --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Server installation completed"
- assert_command_replay "server:install"
-
- installed_php_versions="$(get_installed_php_fpm_versions_for_server "$AWS_TEST_SERVER_NAME")"
- printf '%s\n' "$installed_php_versions" | grep -qx "$primary_php_version"
- printf '%s\n' "$installed_php_versions" | grep -qx "$secondary_php_version"
-}
-
-# ----
-# site:create
-# ----
-
-@test "site:create creates site ${AWS_TEST_SITE_DOMAIN} on AWS provisioned server" {
- require_aws_provision_config
-
- # Cleanup any leftover test site
- cleanup_test_site "$AWS_TEST_SITE_DOMAIN"
-
- run_deployer site:create \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --server="$AWS_TEST_SERVER_NAME" \
- --php-version="$CLOUD_TEST_PHP_PRIMARY_VERSION" \
- --web-root="/"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "site:create"
-}
-
-@test "site:create creates secondary site ${AWS_TEST_SITE_DOMAIN_SECONDARY} on AWS provisioned server" {
- require_aws_provision_config
-
- # Cleanup any leftover secondary test site
- cleanup_test_site "$AWS_TEST_SITE_DOMAIN_SECONDARY"
-
- run_deployer site:create \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
- --server="$AWS_TEST_SERVER_NAME" \
- --php-version="$CLOUD_TEST_PHP_SECONDARY_VERSION" \
- --web-root="/"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "site:create"
-}
-
-# ----
-# aws:dns:set
-# ----
-
-@test "aws:dns:set creates prefixed A record for ${AWS_TEST_DNS_ROOT_FQDN}" {
- require_aws_provision_config
-
- # Get server IP from inventory
- local server_ip
- server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer aws:dns:set \
- --zone="$AWS_TEST_HOSTED_ZONE" \
- --type="A" \
- --name="$AWS_TEST_DNS_ROOT" \
- --value="$server_ip" \
- --ttl="60"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record upserted successfully"
- assert_command_replay "aws:dns:set"
-}
-
-@test "aws:dns:set creates secondary prefixed A record for ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_aws_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer aws:dns:set \
- --zone="$AWS_TEST_HOSTED_ZONE" \
- --type="A" \
- --name="$AWS_TEST_DNS_ROOT_SECONDARY" \
- --value="$server_ip" \
- --ttl="60"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record upserted successfully"
- assert_command_replay "aws:dns:set"
-}
-
-@test "aws:dns:list shows ${AWS_TEST_DNS_ROOT_FQDN} and ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_aws_provision_config
-
- run_deployer aws:dns:list \
- --zone="$AWS_TEST_HOSTED_ZONE"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "$AWS_TEST_HOSTED_ZONE"
- assert_output_contains "$AWS_TEST_DNS_ROOT_FQDN"
- assert_output_contains "$AWS_TEST_DNS_ROOT_SECONDARY_FQDN"
- assert_command_replay "aws:dns:list"
-}
-
-# ----
-# cf:dns:set
-# ----
-
-@test "cf:dns:set creates prefixed A record for ${CF_TEST_DNS_ROOT_FQDN} (non-proxied)" {
- require_aws_provision_config
-
- # Get server IP from inventory
- local server_ip
- server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer cf:dns:set \
- --zone="$CF_TEST_DOMAIN" \
- --type="A" \
- --name="$CF_TEST_DNS_ROOT" \
- --value="$server_ip" \
- --ttl="60" \
- --no-proxied
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record"
- assert_output_contains "successfully"
- assert_command_replay "cf:dns:set"
-}
-
-@test "cf:dns:list shows ${CF_TEST_DNS_ROOT_FQDN}" {
- require_aws_provision_config
-
- run_deployer cf:dns:list \
- --zone="$CF_TEST_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "$CF_TEST_DOMAIN"
- assert_command_replay "cf:dns:list"
-}
-
-# ----
-# site:dns:check
-# ----
-
-@test "site:dns:check resolves DNS for ${AWS_TEST_SITE_DOMAIN}" {
- require_aws_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer site:dns:check \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "Check DNS"
- assert_output_contains "Domain: $AWS_TEST_SITE_DOMAIN"
- assert_output_contains "A:"
- assert_output_contains "AAAA:"
- assert_output_contains "$server_ip"
- assert_command_replay "site:dns:check"
-}
-
-@test "site:dns:check resolves DNS for ${AWS_TEST_SITE_DOMAIN_SECONDARY}" {
- require_aws_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer site:dns:check \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "Check DNS"
- assert_output_contains "Domain: $AWS_TEST_SITE_DOMAIN_SECONDARY"
- assert_output_contains "A:"
- assert_output_contains "AAAA:"
- assert_output_contains "$server_ip"
- assert_command_replay "site:dns:check"
-}
-
-# ----
-# site:shared:push
-# ----
-
-@test "site:shared:push uploads .env to AWS site" {
- require_aws_provision_config
-
- run_deployer site:shared:push \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
- --remote=".env"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file uploaded"
- assert_command_replay "site:shared:push"
-}
-
-@test "site:shared:push uploads .env to AWS secondary site" {
- require_aws_provision_config
-
- run_deployer site:shared:push \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
- --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
- --remote=".env"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file uploaded"
- assert_command_replay "site:shared:push"
-}
-
-# ----
-# site:shared:list
-# ----
-
-@test "site:shared:list shows uploaded shared files for AWS site" {
- require_aws_provision_config
-
- run_deployer site:shared:list \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains ".env"
- assert_command_replay "site:shared:list"
-}
-
-# ----
-# site:shared:pull
-# ----
-
-@test "site:shared:pull downloads .env from AWS site" {
- require_aws_provision_config
-
- local pulled_env="${BATS_TEST_TMPDIR}/aws-shared.env"
-
- run_deployer site:shared:pull \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --remote=".env" \
- --local="$pulled_env" \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file downloaded"
- assert_command_replay "site:shared:pull"
- [[ -f "$pulled_env" ]]
-
- run grep -q "APP_MESSAGE=$CLOUD_TEST_APP_MESSAGE" "$pulled_env"
- [ "$status" -eq 0 ]
-}
-
-# ----
-# site:deploy
-# ----
-
-@test "site:deploy deploys application to AWS site" {
- require_aws_provision_config
-
- # Deploy takes time - use longer timeout
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --repo="$CLOUD_TEST_DEPLOY_REPO" \
- --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Deployment completed"
- assert_command_replay "site:deploy"
-}
-
-@test "site:deploy deploys application to AWS secondary site" {
- require_aws_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
- --repo="$CLOUD_TEST_DEPLOY_REPO" \
- --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Deployment completed"
- assert_command_replay "site:deploy"
-}
-
-# ----
-# cron/supervisor
-# ----
-
-@test "cron:create adds hello.sh cron for AWS site" {
- require_aws_provision_config
-
- run_deployer cron:create \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --schedule="* * * * *"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "cron:create"
-}
-
-@test "cron:sync applies hello.sh cron to AWS server" {
- require_aws_provision_config
-
- run_deployer cron:sync \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "synced to server"
- assert_command_replay "cron:sync"
-}
-
-@test "cron:sync writes AWS crontab entry and log file for hello.sh" {
- require_aws_provision_config
-
- run_deployer server:run \
- --server="$AWS_TEST_SERVER_NAME" \
- --command="sudo -n crontab -l -u deployer | grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT"
-
- run_deployer server:run \
- --server="$AWS_TEST_SERVER_NAME" \
- --command="test -f /var/log/cron/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT.log && echo cron-log-found"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "cron-log-found"
-}
-
-@test "supervisor:create adds hello.sh program for AWS site" {
- require_aws_provision_config
-
- run_deployer supervisor:create \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --no-autostart \
- --no-autorestart \
- --stopwaitsecs="10" \
- --numprocs="1"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "supervisor:create"
-}
-
-@test "supervisor:sync applies hello.sh program to AWS server" {
- require_aws_provision_config
-
- run_deployer supervisor:sync \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "synced to server"
- assert_command_replay "supervisor:sync"
-}
-
-@test "supervisor:sync writes AWS supervisor config for hello.sh" {
- require_aws_provision_config
-
- run_deployer server:run \
- --server="$AWS_TEST_SERVER_NAME" \
- --command="test -f /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT' /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && echo supervisor-config-found"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "supervisor-config-found"
-}
-
-@test "supervisor lifecycle commands restart/stop/start work on AWS server" {
- require_aws_provision_config
-
- run_deployer supervisor:restart \
- --server="$AWS_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:restart"
-
- run_deployer supervisor:stop \
- --server="$AWS_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:stop"
-
- run_deployer supervisor:start \
- --server="$AWS_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:start"
-}
-
-@test "cron:delete removes hello.sh cron for AWS site" {
- require_aws_provision_config
-
- run_deployer cron:delete \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "cron:delete"
-}
-
-@test "cron:sync removes hello.sh crontab entry from AWS server" {
- require_aws_provision_config
-
- run_deployer cron:sync \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "cron:sync"
-
- run_deployer server:run \
- --server="$AWS_TEST_SERVER_NAME" \
- --command="if sudo -n crontab -l -u deployer 2>/dev/null | grep -Fq 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'; then echo cron-entry-present; exit 1; else echo cron-entry-absent; fi"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "cron-entry-absent"
-}
-
-@test "supervisor:delete removes hello.sh program for AWS site" {
- require_aws_provision_config
-
- run_deployer supervisor:delete \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "supervisor:delete"
-}
-
-@test "supervisor:sync removes hello.sh supervisor config from AWS server" {
- require_aws_provision_config
-
- run_deployer supervisor:sync \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:sync"
-
- run_deployer server:run \
- --server="$AWS_TEST_SERVER_NAME" \
- --command="if test -f /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf; then echo supervisor-config-present; exit 1; else echo supervisor-config-absent; fi"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "supervisor-config-absent"
-}
-
-# ----
-# site:https
-# ----
-
-@test "site:https enables HTTPS for ${AWS_TEST_SITE_DOMAIN}" {
- require_aws_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
- --domain="$AWS_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "HTTPS enabled successfully"
- assert_command_replay "site:https"
-}
-
-@test "site:https enables HTTPS for ${AWS_TEST_SITE_DOMAIN_SECONDARY}" {
- require_aws_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "HTTPS enabled successfully"
- assert_command_replay "site:https"
-}
-
-# ----
-# HTTP Verification
-# ----
-
-@test "deployed AWS site responds to HTTP requests after HTTPS setup" {
- require_aws_provision_config
-
- # Verify app response after HTTPS setup.
- wait_for_http "$AWS_TEST_SITE_DOMAIN" "$CLOUD_TEST_APP_MESSAGE" 30
-}
-
-@test "deployed AWS secondary site responds to HTTP requests after HTTPS setup" {
- require_aws_provision_config
-
- wait_for_http "$AWS_TEST_SITE_DOMAIN_SECONDARY" "$CLOUD_TEST_APP_MESSAGE" 30
-}
-
-# ----
-# site:rollback
-# ----
-
-@test "site:rollback shows forward-only deployment guidance" {
- require_aws_provision_config
-
- run_deployer site:rollback
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_info_output
- assert_output_contains "Forward-only deployments"
- assert_bullet_output
-}
-
-# ----
-# site:delete
-# ----
-
-@test "site:delete removes ${AWS_TEST_SITE_DOMAIN_SECONDARY} from server and inventory" {
- require_aws_provision_config
-
- run_deployer site:delete \
- --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "site:delete"
-}
-
-@test "site:delete removes ${AWS_TEST_SITE_DOMAIN} from server and inventory" {
- require_aws_provision_config
-
- run_deployer site:delete \
- --domain="$AWS_TEST_SITE_DOMAIN" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "site:delete"
-}
-
-# ----
-# cf:dns:delete
-# ----
-
-@test "cf:dns:delete removes prefixed A record ${CF_TEST_DNS_ROOT_FQDN}" {
- require_aws_provision_config
-
- run_deployer cf:dns:delete \
- --zone="$CF_TEST_DOMAIN" \
- --type="A" \
- --name="$CF_TEST_DNS_ROOT" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record deleted successfully"
- assert_command_replay "cf:dns:delete"
-}
-
-# ----
-# aws:dns:delete
-# ----
-
-@test "aws:dns:delete removes prefixed A record ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_aws_provision_config
-
- run_deployer aws:dns:delete \
- --zone="$AWS_TEST_HOSTED_ZONE" \
- --type="A" \
- --name="$AWS_TEST_DNS_ROOT_SECONDARY" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record deleted successfully"
- assert_command_replay "aws:dns:delete"
-}
-
-@test "aws:dns:delete removes prefixed A record ${AWS_TEST_DNS_ROOT_FQDN}" {
- require_aws_provision_config
-
- run_deployer aws:dns:delete \
- --zone="$AWS_TEST_HOSTED_ZONE" \
- --type="A" \
- --name="$AWS_TEST_DNS_ROOT" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record deleted successfully"
- assert_command_replay "aws:dns:delete"
-}
-
-# ----
-# Cleanup
-# ----
-
-@test "server:delete removes AWS instance and cleans up resources" {
- require_aws_provision_config
-
- run_deployer server:delete \
- --server="$AWS_TEST_SERVER_NAME" \
- --force \
- --yes \
- --destroy-cloud
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Instance terminated"
- assert_output_contains "Elastic IP released"
- assert_output_contains "removed from inventory"
- assert_command_replay "server:delete"
- aws_assert_no_bats_volumes "$BATS_RUN_SUFFIX"
-}
+# # AWS Integration Tests
+# # Tests: AWS key/provision/DNS + site lifecycle commands
+# #
+# # Prerequisites:
+# # - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION in environment
+# # - Valid AWS credentials with EC2 permissions
+# # - SSH private key at ~/.ssh/id_ed25519
+#
+# load 'lib/helpers'
+# load 'lib/cloud-helpers'
+#
+# # ----
+# # Setup/Teardown
+# # ----
+#
+# setup_file() {
+# require_aws_credentials
+# require_cf_credentials
+# require_aws_dns_prefix_config
+# require_cf_dns_prefix_config
+# cloud_note "Run suffix: ${BATS_RUN_SUFFIX}"
+# aws_cleanup_all
+# cf_cleanup_all
+# }
+#
+# teardown_file() {
+# if ! aws_credentials_available; then
+# return 0
+# fi
+# aws_cleanup_all
+# cf_cleanup_all
+# rm -f "$TEST_INVENTORY"
+# }
+#
+# setup() {
+# cloud_check_failed
+# require_aws_credentials
+# }
+#
+# teardown() {
+# cloud_mark_failed
+# }
+#
+# # ----
+# # aws:key:add
+# # ----
+#
+# @test "aws:key:add uploads public key to AWS" {
+# run_deployer aws:key:add \
+# --name="$AWS_TEST_KEY_NAME" \
+# --public-key-path="$CLOUD_TEST_KEY_PATH"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Key pair imported successfully"
+# assert_output_contains "Name: $AWS_TEST_KEY_NAME"
+# assert_command_replay "aws:key:add"
+# }
+#
+# # ----
+# # aws:key:list
+# # ----
+#
+# @test "aws:key:list shows uploaded key" {
+# run_deployer aws:key:list
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "$AWS_TEST_KEY_NAME"
+# assert_command_replay "aws:key:list"
+# }
+#
+# # ----
+# # aws:key:delete
+# # ----
+#
+# @test "aws:key:delete removes key from AWS" {
+# run_deployer aws:key:delete \
+# --key="$AWS_TEST_KEY_NAME" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Key pair deleted successfully"
+# assert_command_replay "aws:key:delete"
+# }
+#
+# @test "aws:key:list confirms key deleted" {
+# run_deployer aws:key:list
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_not_contains "$AWS_TEST_KEY_NAME"
+# }
+#
+# # ----
+# # aws:provision
+# # ----
+#
+# @test "aws:provision creates EC2 instance and adds to inventory" {
+# require_aws_provision_config
+#
+# # Cleanup any leftover test server
+# aws_cleanup_test_server
+#
+# run_deployer aws:provision \
+# --name="$AWS_TEST_SERVER_NAME" \
+# --instance-type="$AWS_TEST_INSTANCE_TYPE" \
+# --image="$AWS_TEST_IMAGE" \
+# --key-pair="$AWS_TEST_KEY_PAIR" \
+# --private-key-path="$AWS_TEST_PRIVATE_KEY_PATH" \
+# --vpc="$AWS_TEST_VPC" \
+# --subnet="$AWS_TEST_SUBNET" \
+# --disk-size="$AWS_TEST_DISK_SIZE" \
+# --no-monitoring
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Instance provisioned"
+# assert_output_contains "Instance is running"
+# assert_output_contains "Elastic IP allocated"
+# assert_output_contains "Server added to inventory"
+# assert_command_replay "aws:provision"
+#
+# # Tag contract for janitor discovery
+# aws_assert_instance_tag_contract "$AWS_TEST_SERVER_NAME"
+# aws_assert_eip_tag_contract "$AWS_TEST_SERVER_NAME"
+# aws_assert_root_volume_tag_contract "$AWS_TEST_SERVER_NAME"
+# }
+#
+# @test "server:install configures AWS provisioned server" {
+# require_aws_provision_config
+#
+# local primary_php_version secondary_php_version installed_php_versions
+# primary_php_version="$CLOUD_TEST_PHP_PRIMARY_VERSION"
+# secondary_php_version="$CLOUD_TEST_PHP_SECONDARY_VERSION"
+#
+# # Full install takes time - use longer timeout
+# run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --generate-deploy-key \
+# --timezone="UTC" \
+# --php-version="$primary_php_version" \
+# --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Server installation completed"
+# assert_output_contains "public key"
+# assert_command_replay "server:install"
+#
+# # Install secondary PHP-FPM version on the same server (keep primary as default)
+# run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --generate-deploy-key \
+# --timezone="UTC" \
+# --php-version="$secondary_php_version" \
+# --no-php-default \
+# --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Server installation completed"
+# assert_command_replay "server:install"
+#
+# installed_php_versions="$(get_installed_php_fpm_versions_for_server "$AWS_TEST_SERVER_NAME")"
+# printf '%s\n' "$installed_php_versions" | grep -qx "$primary_php_version"
+# printf '%s\n' "$installed_php_versions" | grep -qx "$secondary_php_version"
+# }
+#
+# # ----
+# # site:create
+# # ----
+#
+# @test "site:create creates site ${AWS_TEST_SITE_DOMAIN} on AWS provisioned server" {
+# require_aws_provision_config
+#
+# # Cleanup any leftover test site
+# cleanup_test_site "$AWS_TEST_SITE_DOMAIN"
+#
+# run_deployer site:create \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --php-version="$CLOUD_TEST_PHP_PRIMARY_VERSION" \
+# --web-root="/"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "site:create"
+# }
+#
+# @test "site:create creates secondary site ${AWS_TEST_SITE_DOMAIN_SECONDARY} on AWS provisioned server" {
+# require_aws_provision_config
+#
+# # Cleanup any leftover secondary test site
+# cleanup_test_site "$AWS_TEST_SITE_DOMAIN_SECONDARY"
+#
+# run_deployer site:create \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --php-version="$CLOUD_TEST_PHP_SECONDARY_VERSION" \
+# --web-root="/"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "site:create"
+# }
+#
+# # ----
+# # aws:dns:set
+# # ----
+#
+# @test "aws:dns:set creates prefixed A record for ${AWS_TEST_DNS_ROOT_FQDN}" {
+# require_aws_provision_config
+#
+# # Get server IP from inventory
+# local server_ip
+# server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer aws:dns:set \
+# --zone="$AWS_TEST_HOSTED_ZONE" \
+# --type="A" \
+# --name="$AWS_TEST_DNS_ROOT" \
+# --value="$server_ip" \
+# --ttl="60"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record upserted successfully"
+# assert_command_replay "aws:dns:set"
+# }
+#
+# @test "aws:dns:set creates secondary prefixed A record for ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_aws_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer aws:dns:set \
+# --zone="$AWS_TEST_HOSTED_ZONE" \
+# --type="A" \
+# --name="$AWS_TEST_DNS_ROOT_SECONDARY" \
+# --value="$server_ip" \
+# --ttl="60"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record upserted successfully"
+# assert_command_replay "aws:dns:set"
+# }
+#
+# @test "aws:dns:list shows ${AWS_TEST_DNS_ROOT_FQDN} and ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_aws_provision_config
+#
+# run_deployer aws:dns:list \
+# --zone="$AWS_TEST_HOSTED_ZONE"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "$AWS_TEST_HOSTED_ZONE"
+# assert_output_contains "$AWS_TEST_DNS_ROOT_FQDN"
+# assert_output_contains "$AWS_TEST_DNS_ROOT_SECONDARY_FQDN"
+# assert_command_replay "aws:dns:list"
+# }
+#
+# # ----
+# # cf:dns:set
+# # ----
+#
+# @test "cf:dns:set creates prefixed A record for ${CF_TEST_DNS_ROOT_FQDN} (non-proxied)" {
+# require_aws_provision_config
+#
+# # Get server IP from inventory
+# local server_ip
+# server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer cf:dns:set \
+# --zone="$CF_TEST_DOMAIN" \
+# --type="A" \
+# --name="$CF_TEST_DNS_ROOT" \
+# --value="$server_ip" \
+# --ttl="60" \
+# --no-proxied
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record"
+# assert_output_contains "successfully"
+# assert_command_replay "cf:dns:set"
+# }
+#
+# @test "cf:dns:list shows ${CF_TEST_DNS_ROOT_FQDN}" {
+# require_aws_provision_config
+#
+# run_deployer cf:dns:list \
+# --zone="$CF_TEST_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "$CF_TEST_DOMAIN"
+# assert_command_replay "cf:dns:list"
+# }
+#
+# # ----
+# # site:dns:check
+# # ----
+#
+# @test "site:dns:check resolves DNS for ${AWS_TEST_SITE_DOMAIN}" {
+# require_aws_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer site:dns:check \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "Check DNS"
+# assert_output_contains "Domain: $AWS_TEST_SITE_DOMAIN"
+# assert_output_contains "A:"
+# assert_output_contains "AAAA:"
+# assert_output_contains "$server_ip"
+# assert_command_replay "site:dns:check"
+# }
+#
+# @test "site:dns:check resolves DNS for ${AWS_TEST_SITE_DOMAIN_SECONDARY}" {
+# require_aws_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$AWS_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer site:dns:check \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "Check DNS"
+# assert_output_contains "Domain: $AWS_TEST_SITE_DOMAIN_SECONDARY"
+# assert_output_contains "A:"
+# assert_output_contains "AAAA:"
+# assert_output_contains "$server_ip"
+# assert_command_replay "site:dns:check"
+# }
+#
+# # ----
+# # site:shared:push
+# # ----
+#
+# @test "site:shared:push uploads .env to AWS site" {
+# require_aws_provision_config
+#
+# run_deployer site:shared:push \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
+# --remote=".env"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file uploaded"
+# assert_command_replay "site:shared:push"
+# }
+#
+# @test "site:shared:push uploads .env to AWS secondary site" {
+# require_aws_provision_config
+#
+# run_deployer site:shared:push \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
+# --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
+# --remote=".env"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file uploaded"
+# assert_command_replay "site:shared:push"
+# }
+#
+# # ----
+# # site:shared:list
+# # ----
+#
+# @test "site:shared:list shows uploaded shared files for AWS site" {
+# require_aws_provision_config
+#
+# run_deployer site:shared:list \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains ".env"
+# assert_command_replay "site:shared:list"
+# }
+#
+# # ----
+# # site:shared:pull
+# # ----
+#
+# @test "site:shared:pull downloads .env from AWS site" {
+# require_aws_provision_config
+#
+# local pulled_env="${BATS_TEST_TMPDIR}/aws-shared.env"
+#
+# run_deployer site:shared:pull \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --remote=".env" \
+# --local="$pulled_env" \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file downloaded"
+# assert_command_replay "site:shared:pull"
+# [[ -f "$pulled_env" ]]
+#
+# run grep -q "APP_MESSAGE=$CLOUD_TEST_APP_MESSAGE" "$pulled_env"
+# [ "$status" -eq 0 ]
+# }
+#
+# # ----
+# # site:deploy
+# # ----
+#
+# @test "site:deploy deploys application to AWS site" {
+# require_aws_provision_config
+#
+# # Deploy takes time - use longer timeout
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --repo="$CLOUD_TEST_DEPLOY_REPO" \
+# --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Deployment completed"
+# assert_command_replay "site:deploy"
+# }
+#
+# @test "site:deploy deploys application to AWS secondary site" {
+# require_aws_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
+# --repo="$CLOUD_TEST_DEPLOY_REPO" \
+# --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Deployment completed"
+# assert_command_replay "site:deploy"
+# }
+#
+# # ----
+# # cron/supervisor
+# # ----
+#
+# @test "cron:create adds hello.sh cron for AWS site" {
+# require_aws_provision_config
+#
+# run_deployer cron:create \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --schedule="* * * * *"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "cron:create"
+# }
+#
+# @test "cron:sync applies hello.sh cron to AWS server" {
+# require_aws_provision_config
+#
+# run_deployer cron:sync \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "synced to server"
+# assert_command_replay "cron:sync"
+# }
+#
+# @test "cron:sync writes AWS crontab entry and log file for hello.sh" {
+# require_aws_provision_config
+#
+# run_deployer server:run \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --command="sudo -n crontab -l -u deployer | grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT"
+#
+# run_deployer server:run \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --command="test -f /var/log/cron/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT.log && echo cron-log-found"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "cron-log-found"
+# }
+#
+# @test "supervisor:create adds hello.sh program for AWS site" {
+# require_aws_provision_config
+#
+# run_deployer supervisor:create \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --no-autostart \
+# --no-autorestart \
+# --stopwaitsecs="10" \
+# --numprocs="1"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "supervisor:create"
+# }
+#
+# @test "supervisor:sync applies hello.sh program to AWS server" {
+# require_aws_provision_config
+#
+# run_deployer supervisor:sync \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "synced to server"
+# assert_command_replay "supervisor:sync"
+# }
+#
+# @test "supervisor:sync writes AWS supervisor config for hello.sh" {
+# require_aws_provision_config
+#
+# run_deployer server:run \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --command="test -f /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT' /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && echo supervisor-config-found"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "supervisor-config-found"
+# }
+#
+# @test "supervisor lifecycle commands restart/stop/start work on AWS server" {
+# require_aws_provision_config
+#
+# run_deployer supervisor:restart \
+# --server="$AWS_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:restart"
+#
+# run_deployer supervisor:stop \
+# --server="$AWS_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:stop"
+#
+# run_deployer supervisor:start \
+# --server="$AWS_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:start"
+# }
+#
+# @test "cron:delete removes hello.sh cron for AWS site" {
+# require_aws_provision_config
+#
+# run_deployer cron:delete \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "cron:delete"
+# }
+#
+# @test "cron:sync removes hello.sh crontab entry from AWS server" {
+# require_aws_provision_config
+#
+# run_deployer cron:sync \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "cron:sync"
+#
+# run_deployer server:run \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --command="if sudo -n crontab -l -u deployer 2>/dev/null | grep -Fq 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'; then echo cron-entry-present; exit 1; else echo cron-entry-absent; fi"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "cron-entry-absent"
+# }
+#
+# @test "supervisor:delete removes hello.sh program for AWS site" {
+# require_aws_provision_config
+#
+# run_deployer supervisor:delete \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "supervisor:delete"
+# }
+#
+# @test "supervisor:sync removes hello.sh supervisor config from AWS server" {
+# require_aws_provision_config
+#
+# run_deployer supervisor:sync \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:sync"
+#
+# run_deployer server:run \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --command="if test -f /etc/supervisor/conf.d/$AWS_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf; then echo supervisor-config-present; exit 1; else echo supervisor-config-absent; fi"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "supervisor-config-absent"
+# }
+#
+# # ----
+# # site:https
+# # ----
+#
+# @test "site:https enables HTTPS for ${AWS_TEST_SITE_DOMAIN}" {
+# require_aws_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
+# --domain="$AWS_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "HTTPS enabled successfully"
+# assert_command_replay "site:https"
+# }
+#
+# @test "site:https enables HTTPS for ${AWS_TEST_SITE_DOMAIN_SECONDARY}" {
+# require_aws_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "HTTPS enabled successfully"
+# assert_command_replay "site:https"
+# }
+#
+# # ----
+# # HTTP Verification
+# # ----
+#
+# @test "deployed AWS site responds to HTTP requests after HTTPS setup" {
+# require_aws_provision_config
+#
+# # Verify app response after HTTPS setup.
+# wait_for_http "$AWS_TEST_SITE_DOMAIN" "$CLOUD_TEST_APP_MESSAGE" 30
+# }
+#
+# @test "deployed AWS secondary site responds to HTTP requests after HTTPS setup" {
+# require_aws_provision_config
+#
+# wait_for_http "$AWS_TEST_SITE_DOMAIN_SECONDARY" "$CLOUD_TEST_APP_MESSAGE" 30
+# }
+#
+# # ----
+# # site:rollback
+# # ----
+#
+# @test "site:rollback shows forward-only deployment guidance" {
+# require_aws_provision_config
+#
+# run_deployer site:rollback
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_info_output
+# assert_output_contains "Forward-only deployments"
+# assert_bullet_output
+# }
+#
+# # ----
+# # site:delete
+# # ----
+#
+# @test "site:delete removes ${AWS_TEST_SITE_DOMAIN_SECONDARY} from server and inventory" {
+# require_aws_provision_config
+#
+# run_deployer site:delete \
+# --domain="$AWS_TEST_SITE_DOMAIN_SECONDARY" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "site:delete"
+# }
+#
+# @test "site:delete removes ${AWS_TEST_SITE_DOMAIN} from server and inventory" {
+# require_aws_provision_config
+#
+# run_deployer site:delete \
+# --domain="$AWS_TEST_SITE_DOMAIN" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "site:delete"
+# }
+#
+# # ----
+# # cf:dns:delete
+# # ----
+#
+# @test "cf:dns:delete removes prefixed A record ${CF_TEST_DNS_ROOT_FQDN}" {
+# require_aws_provision_config
+#
+# run_deployer cf:dns:delete \
+# --zone="$CF_TEST_DOMAIN" \
+# --type="A" \
+# --name="$CF_TEST_DNS_ROOT" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record deleted successfully"
+# assert_command_replay "cf:dns:delete"
+# }
+#
+# # ----
+# # aws:dns:delete
+# # ----
+#
+# @test "aws:dns:delete removes prefixed A record ${AWS_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_aws_provision_config
+#
+# run_deployer aws:dns:delete \
+# --zone="$AWS_TEST_HOSTED_ZONE" \
+# --type="A" \
+# --name="$AWS_TEST_DNS_ROOT_SECONDARY" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record deleted successfully"
+# assert_command_replay "aws:dns:delete"
+# }
+#
+# @test "aws:dns:delete removes prefixed A record ${AWS_TEST_DNS_ROOT_FQDN}" {
+# require_aws_provision_config
+#
+# run_deployer aws:dns:delete \
+# --zone="$AWS_TEST_HOSTED_ZONE" \
+# --type="A" \
+# --name="$AWS_TEST_DNS_ROOT" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record deleted successfully"
+# assert_command_replay "aws:dns:delete"
+# }
+#
+# # ----
+# # Cleanup
+# # ----
+#
+# @test "server:delete removes AWS instance and cleans up resources" {
+# require_aws_provision_config
+#
+# run_deployer server:delete \
+# --server="$AWS_TEST_SERVER_NAME" \
+# --force \
+# --yes \
+# --destroy-cloud
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Instance terminated"
+# assert_output_contains "Elastic IP released"
+# assert_output_contains "removed from inventory"
+# assert_command_replay "server:delete"
+# aws_assert_no_bats_volumes "$BATS_RUN_SUFFIX"
+# }
diff --git a/tests/bats/cloud-do.bats b/tests/bats/cloud-do.bats
index 1e566cf..9746c63 100644
--- a/tests/bats/cloud-do.bats
+++ b/tests/bats/cloud-do.bats
@@ -1,842 +1,847 @@
#!/usr/bin/env bats
-# DigitalOcean Integration Tests
-# Tests: DigitalOcean key/provision/DNS + site lifecycle commands
+# NOTE: These tests are disabled because the cloud provider accounts
+# (AWS, DigitalOcean, Cloudflare) and test domains they depend on have
+# been spun down. Uncomment the lines below to re-enable them once
+# active credentials are available again.
#
-# Prerequisites:
-# - DIGITALOCEAN_API_TOKEN or DO_API_TOKEN in environment
-# - Valid DigitalOcean API token with droplet permissions
-# - SSH private key at ~/.ssh/id_ed25519
-
-load 'lib/helpers'
-load 'lib/cloud-helpers'
-
-# ----
-# Setup/Teardown
-# ----
-
-setup_file() {
- require_do_credentials
- require_do_dns_prefix_config
- cloud_note "Run suffix: ${BATS_RUN_SUFFIX}"
- do_cleanup_all
-}
-
-teardown_file() {
- if ! do_credentials_available; then
- return 0
- fi
- do_cleanup_all
- rm -f "$TEST_INVENTORY"
-}
-
-setup() {
- cloud_check_failed
- require_do_credentials
-}
-
-teardown() {
- cloud_mark_failed
-}
-
-# ----
-# do:key:add
-# ----
-
-@test "do:key:add uploads public key to DigitalOcean" {
- run_deployer do:key:add \
- --name="$DO_TEST_KEY_NAME" \
- --public-key-path="$CLOUD_TEST_KEY_PATH"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Public SSH key uploaded successfully"
- assert_output_contains "ID:"
- assert_command_replay "do:key:add"
-}
-
-# ----
-# do:key:list
-# ----
-
-@test "do:key:list shows uploaded key" {
- run_deployer do:key:list
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "$DO_TEST_KEY_NAME"
- assert_command_replay "do:key:list"
-}
-
-# ----
-# do:key:delete
-# ----
-
-@test "do:key:delete removes key from DigitalOcean" {
- # Find key ID by name (safe: only deletes key we created)
- local key_id
- key_id=$(do_find_key_id_by_name "$DO_TEST_KEY_NAME")
-
- # Safety: only proceed if we found a key with our test name
- [[ -n "$key_id" ]] || skip "Test key not found"
-
- run_deployer do:key:delete \
- --key="$key_id" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Public SSH key deleted successfully"
- assert_command_replay "do:key:delete"
-}
-
-@test "do:key:list confirms key deleted" {
- run_deployer do:key:list
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_not_contains "$DO_TEST_KEY_NAME"
-}
-
-# ----
-# do:provision
-# ----
-
-@test "do:provision creates droplet and adds to inventory" {
- require_do_provision_config
-
- # Cleanup any leftover test server
- do_cleanup_test_server
-
- run_deployer do:provision \
- --name="$DO_TEST_SERVER_NAME" \
- --region="$DO_TEST_REGION" \
- --size="$DO_TEST_SIZE" \
- --image="$DO_TEST_IMAGE" \
- --ssh-key-id="$DO_TEST_SSH_KEY_ID" \
- --private-key-path="$DO_TEST_PRIVATE_KEY_PATH" \
- --no-backups \
- --monitoring \
- --ipv6 \
- --vpc-uuid="$DO_TEST_VPC_UUID"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Droplet provisioned"
- assert_output_contains "Droplet is active"
- assert_output_contains "Server added to inventory"
- assert_command_replay "do:provision"
-
- # Tag contract for janitor discovery
- do_assert_droplet_tag_contract "$DO_TEST_SERVER_NAME"
-}
-
-@test "server:install configures DigitalOcean provisioned server" {
- require_do_provision_config
-
- local primary_php_version secondary_php_version installed_php_versions
- primary_php_version="$CLOUD_TEST_PHP_PRIMARY_VERSION"
- secondary_php_version="$CLOUD_TEST_PHP_SECONDARY_VERSION"
-
- # Full install takes time - use longer timeout
- run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
- --server="$DO_TEST_SERVER_NAME" \
- --generate-deploy-key \
- --timezone="UTC" \
- --php-version="$primary_php_version" \
- --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Server installation completed"
- assert_output_contains "public key"
- assert_command_replay "server:install"
-
- # Install secondary PHP-FPM version on the same server (keep primary as default)
- run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
- --server="$DO_TEST_SERVER_NAME" \
- --generate-deploy-key \
- --timezone="UTC" \
- --php-version="$secondary_php_version" \
- --no-php-default \
- --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Server installation completed"
- assert_command_replay "server:install"
-
- installed_php_versions="$(get_installed_php_fpm_versions_for_server "$DO_TEST_SERVER_NAME")"
- printf '%s\n' "$installed_php_versions" | grep -qx "$primary_php_version"
- printf '%s\n' "$installed_php_versions" | grep -qx "$secondary_php_version"
-}
-
-# ----
-# site:create
-# ----
-
-@test "site:create creates site ${DO_TEST_SITE_DOMAIN} on DigitalOcean provisioned server" {
- require_do_provision_config
-
- # Cleanup any leftover test site
- cleanup_test_site "$DO_TEST_SITE_DOMAIN"
-
- run_deployer site:create \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --server="$DO_TEST_SERVER_NAME" \
- --php-version="$CLOUD_TEST_PHP_PRIMARY_VERSION" \
- --web-root="/"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "site:create"
-}
-
-@test "site:create creates secondary site ${DO_TEST_SITE_DOMAIN_SECONDARY} on DigitalOcean provisioned server" {
- require_do_provision_config
-
- # Cleanup any leftover secondary test site
- cleanup_test_site "$DO_TEST_SITE_DOMAIN_SECONDARY"
-
- run_deployer site:create \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
- --server="$DO_TEST_SERVER_NAME" \
- --php-version="$CLOUD_TEST_PHP_SECONDARY_VERSION" \
- --web-root="/"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "site:create"
-}
-
-# ----
-# do:dns:set
-# ----
-
-@test "do:dns:set creates prefixed A record for ${DO_TEST_DNS_ROOT_FQDN}" {
- require_do_provision_config
-
- # Get server IP from inventory
- local server_ip
- server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer do:dns:set \
- --zone="$DO_TEST_DOMAIN" \
- --type="A" \
- --name="$DO_TEST_DNS_ROOT" \
- --value="$server_ip" \
- --ttl="60"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record"
- assert_output_contains "successfully"
- assert_command_replay "do:dns:set"
-}
-
-@test "do:dns:set creates secondary prefixed A record for ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_do_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer do:dns:set \
- --zone="$DO_TEST_DOMAIN" \
- --type="A" \
- --name="$DO_TEST_DNS_ROOT_SECONDARY" \
- --value="$server_ip" \
- --ttl="60"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record"
- assert_output_contains "successfully"
- assert_command_replay "do:dns:set"
-}
-
-@test "do:dns:list shows ${DO_TEST_DNS_ROOT_FQDN} and ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_do_provision_config
-
- run_deployer do:dns:list \
- --zone="$DO_TEST_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "$DO_TEST_DOMAIN"
- assert_output_contains "$DO_TEST_DNS_ROOT"
- assert_output_contains "$DO_TEST_DNS_ROOT_SECONDARY"
- assert_command_replay "do:dns:list"
-}
-
-# ----
-# site:dns:check
-# ----
-
-@test "site:dns:check resolves DNS for ${DO_TEST_SITE_DOMAIN}" {
- require_do_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer site:dns:check \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "Check DNS"
- assert_output_contains "Domain: $DO_TEST_SITE_DOMAIN"
- assert_output_contains "A:"
- assert_output_contains "AAAA:"
- assert_output_contains "$server_ip"
- assert_command_replay "site:dns:check"
-}
-
-@test "site:dns:check resolves DNS for ${DO_TEST_SITE_DOMAIN_SECONDARY}" {
- require_do_provision_config
-
- local server_ip
- server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
-
- [[ -n "$server_ip" ]] || skip "Could not determine server IP"
-
- run_deployer site:dns:check \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "Check DNS"
- assert_output_contains "Domain: $DO_TEST_SITE_DOMAIN_SECONDARY"
- assert_output_contains "A:"
- assert_output_contains "AAAA:"
- assert_output_contains "$server_ip"
- assert_command_replay "site:dns:check"
-}
-
-# ----
-# site:shared:push
-# ----
-
-@test "site:shared:push uploads .env to DigitalOcean site" {
- require_do_provision_config
-
- run_deployer site:shared:push \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
- --remote=".env"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file uploaded"
- assert_command_replay "site:shared:push"
-}
-
-@test "site:shared:push uploads .env to DigitalOcean secondary site" {
- require_do_provision_config
-
- run_deployer site:shared:push \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
- --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
- --remote=".env"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file uploaded"
- assert_command_replay "site:shared:push"
-}
-
-# ----
-# site:shared:list
-# ----
-
-@test "site:shared:list shows uploaded shared files for DigitalOcean site" {
- require_do_provision_config
-
- run_deployer site:shared:list \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains ".env"
- assert_command_replay "site:shared:list"
-}
-
-# ----
-# site:shared:pull
-# ----
-
-@test "site:shared:pull downloads .env from DigitalOcean site" {
- require_do_provision_config
-
- local pulled_env="${BATS_TEST_TMPDIR}/do-shared.env"
-
- run_deployer site:shared:pull \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --remote=".env" \
- --local="$pulled_env" \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Shared file downloaded"
- assert_command_replay "site:shared:pull"
- [[ -f "$pulled_env" ]]
-
- run grep -q "APP_MESSAGE=$CLOUD_TEST_APP_MESSAGE" "$pulled_env"
- [ "$status" -eq 0 ]
-}
-
-# ----
-# site:deploy
-# ----
-
-@test "site:deploy deploys application to DigitalOcean site" {
- require_do_provision_config
-
- # Deploy takes time - use longer timeout
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --repo="$CLOUD_TEST_DEPLOY_REPO" \
- --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Deployment completed"
- assert_command_replay "site:deploy"
-}
-
-@test "site:deploy deploys application to DigitalOcean secondary site" {
- require_do_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
- --repo="$CLOUD_TEST_DEPLOY_REPO" \
- --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Deployment completed"
- assert_command_replay "site:deploy"
-}
-
-# ----
-# cron/supervisor
-# ----
-
-@test "cron:create adds hello.sh cron for DigitalOcean site" {
- require_do_provision_config
-
- run_deployer cron:create \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --schedule="* * * * *"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "cron:create"
-}
-
-@test "cron:sync applies hello.sh cron to DigitalOcean server" {
- require_do_provision_config
-
- run_deployer cron:sync \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "synced to server"
- assert_command_replay "cron:sync"
-}
-
-@test "cron:sync writes DigitalOcean crontab entry and log file for hello.sh" {
- require_do_provision_config
-
- run_deployer server:run \
- --server="$DO_TEST_SERVER_NAME" \
- --command="sudo -n crontab -l -u deployer | grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT"
-
- run_deployer server:run \
- --server="$DO_TEST_SERVER_NAME" \
- --command="test -f /var/log/cron/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT.log && echo cron-log-found"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "cron-log-found"
-}
-
-@test "supervisor:create adds hello.sh program for DigitalOcean site" {
- require_do_provision_config
-
- run_deployer supervisor:create \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --no-autostart \
- --no-autorestart \
- --stopwaitsecs="10" \
- --numprocs="1"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "added to inventory"
- assert_command_replay "supervisor:create"
-}
-
-@test "supervisor:sync applies hello.sh program to DigitalOcean server" {
- require_do_provision_config
-
- run_deployer supervisor:sync \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "synced to server"
- assert_command_replay "supervisor:sync"
-}
-
-@test "supervisor:sync writes DigitalOcean supervisor config for hello.sh" {
- require_do_provision_config
-
- run_deployer server:run \
- --server="$DO_TEST_SERVER_NAME" \
- --command="test -f /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT' /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && echo supervisor-config-found"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "supervisor-config-found"
-}
-
-@test "supervisor lifecycle commands restart/stop/start work on DigitalOcean server" {
- require_do_provision_config
-
- run_deployer supervisor:restart \
- --server="$DO_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:restart"
-
- run_deployer supervisor:stop \
- --server="$DO_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:stop"
-
- run_deployer supervisor:start \
- --server="$DO_TEST_SERVER_NAME"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:start"
-}
-
-@test "cron:delete removes hello.sh cron for DigitalOcean site" {
- require_do_provision_config
-
- run_deployer cron:delete \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "cron:delete"
-}
-
-@test "cron:sync removes hello.sh crontab entry from DigitalOcean server" {
- require_do_provision_config
-
- run_deployer cron:sync \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "cron:sync"
-
- run_deployer server:run \
- --server="$DO_TEST_SERVER_NAME" \
- --command="if sudo -n crontab -l -u deployer 2>/dev/null | grep -Fq 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'; then echo cron-entry-present; exit 1; else echo cron-entry-absent; fi"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "cron-entry-absent"
-}
-
-@test "supervisor:delete removes hello.sh program for DigitalOcean site" {
- require_do_provision_config
-
- run_deployer supervisor:delete \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "supervisor:delete"
-}
-
-@test "supervisor:sync removes hello.sh supervisor config from DigitalOcean server" {
- require_do_provision_config
-
- run_deployer supervisor:sync \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_command_replay "supervisor:sync"
-
- run_deployer server:run \
- --server="$DO_TEST_SERVER_NAME" \
- --command="if test -f /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf; then echo supervisor-config-present; exit 1; else echo supervisor-config-absent; fi"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_output_contains "supervisor-config-absent"
-}
-
-# ----
-# site:https
-# ----
-
-@test "site:https enables HTTPS for ${DO_TEST_SITE_DOMAIN}" {
- require_do_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
- --domain="$DO_TEST_SITE_DOMAIN"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "HTTPS enabled successfully"
- assert_command_replay "site:https"
-}
-
-@test "site:https enables HTTPS for ${DO_TEST_SITE_DOMAIN_SECONDARY}" {
- require_do_provision_config
-
- run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY"
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "HTTPS enabled successfully"
- assert_command_replay "site:https"
-}
-
-# ----
-# HTTP Verification
-# ----
-
-@test "deployed DigitalOcean site responds to HTTP requests after HTTPS setup" {
- require_do_provision_config
-
- # Verify app response after HTTPS setup.
- wait_for_http "$DO_TEST_SITE_DOMAIN" "$CLOUD_TEST_APP_MESSAGE" 30
-}
-
-@test "deployed DigitalOcean secondary site responds to HTTP requests after HTTPS setup" {
- require_do_provision_config
-
- wait_for_http "$DO_TEST_SITE_DOMAIN_SECONDARY" "$CLOUD_TEST_APP_MESSAGE" 30
-}
-
-# ----
-# site:rollback
-# ----
-
-@test "site:rollback shows forward-only deployment guidance" {
- require_do_provision_config
-
- run_deployer site:rollback
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_info_output
- assert_output_contains "Forward-only deployments"
- assert_bullet_output
-}
-
-# ----
-# site:delete
-# ----
-
-@test "site:delete removes ${DO_TEST_SITE_DOMAIN_SECONDARY} from server and inventory" {
- require_do_provision_config
-
- run_deployer site:delete \
- --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "site:delete"
-}
-
-@test "site:delete removes ${DO_TEST_SITE_DOMAIN} from server and inventory" {
- require_do_provision_config
-
- run_deployer site:delete \
- --domain="$DO_TEST_SITE_DOMAIN" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "removed from inventory"
- assert_command_replay "site:delete"
-}
-
-# ----
-# do:dns:delete
-# ----
-
-@test "do:dns:delete removes prefixed A record ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
- require_do_provision_config
-
- run_deployer do:dns:delete \
- --zone="$DO_TEST_DOMAIN" \
- --type="A" \
- --name="$DO_TEST_DNS_ROOT_SECONDARY" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record deleted successfully"
- assert_command_replay "do:dns:delete"
-}
-
-@test "do:dns:delete removes prefixed A record ${DO_TEST_DNS_ROOT_FQDN}" {
- require_do_provision_config
-
- run_deployer do:dns:delete \
- --zone="$DO_TEST_DOMAIN" \
- --type="A" \
- --name="$DO_TEST_DNS_ROOT" \
- --force \
- --yes
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "DNS record deleted successfully"
- assert_command_replay "do:dns:delete"
-}
-
-# ----
-# Cleanup
-# ----
-
-@test "server:delete removes DigitalOcean droplet" {
- require_do_provision_config
-
- run_deployer server:delete \
- --server="$DO_TEST_SERVER_NAME" \
- --force \
- --yes \
- --destroy-cloud
-
- debug_output
-
- [ "$status" -eq 0 ]
- assert_success_output
- assert_output_contains "Droplet destroyed"
- assert_output_contains "removed from inventory"
- assert_command_replay "server:delete"
-}
+# # DigitalOcean Integration Tests
+# # Tests: DigitalOcean key/provision/DNS + site lifecycle commands
+# #
+# # Prerequisites:
+# # - DIGITALOCEAN_API_TOKEN or DO_API_TOKEN in environment
+# # - Valid DigitalOcean API token with droplet permissions
+# # - SSH private key at ~/.ssh/id_ed25519
+#
+# load 'lib/helpers'
+# load 'lib/cloud-helpers'
+#
+# # ----
+# # Setup/Teardown
+# # ----
+#
+# setup_file() {
+# require_do_credentials
+# require_do_dns_prefix_config
+# cloud_note "Run suffix: ${BATS_RUN_SUFFIX}"
+# do_cleanup_all
+# }
+#
+# teardown_file() {
+# if ! do_credentials_available; then
+# return 0
+# fi
+# do_cleanup_all
+# rm -f "$TEST_INVENTORY"
+# }
+#
+# setup() {
+# cloud_check_failed
+# require_do_credentials
+# }
+#
+# teardown() {
+# cloud_mark_failed
+# }
+#
+# # ----
+# # do:key:add
+# # ----
+#
+# @test "do:key:add uploads public key to DigitalOcean" {
+# run_deployer do:key:add \
+# --name="$DO_TEST_KEY_NAME" \
+# --public-key-path="$CLOUD_TEST_KEY_PATH"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Public SSH key uploaded successfully"
+# assert_output_contains "ID:"
+# assert_command_replay "do:key:add"
+# }
+#
+# # ----
+# # do:key:list
+# # ----
+#
+# @test "do:key:list shows uploaded key" {
+# run_deployer do:key:list
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "$DO_TEST_KEY_NAME"
+# assert_command_replay "do:key:list"
+# }
+#
+# # ----
+# # do:key:delete
+# # ----
+#
+# @test "do:key:delete removes key from DigitalOcean" {
+# # Find key ID by name (safe: only deletes key we created)
+# local key_id
+# key_id=$(do_find_key_id_by_name "$DO_TEST_KEY_NAME")
+#
+# # Safety: only proceed if we found a key with our test name
+# [[ -n "$key_id" ]] || skip "Test key not found"
+#
+# run_deployer do:key:delete \
+# --key="$key_id" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Public SSH key deleted successfully"
+# assert_command_replay "do:key:delete"
+# }
+#
+# @test "do:key:list confirms key deleted" {
+# run_deployer do:key:list
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_not_contains "$DO_TEST_KEY_NAME"
+# }
+#
+# # ----
+# # do:provision
+# # ----
+#
+# @test "do:provision creates droplet and adds to inventory" {
+# require_do_provision_config
+#
+# # Cleanup any leftover test server
+# do_cleanup_test_server
+#
+# run_deployer do:provision \
+# --name="$DO_TEST_SERVER_NAME" \
+# --region="$DO_TEST_REGION" \
+# --size="$DO_TEST_SIZE" \
+# --image="$DO_TEST_IMAGE" \
+# --ssh-key-id="$DO_TEST_SSH_KEY_ID" \
+# --private-key-path="$DO_TEST_PRIVATE_KEY_PATH" \
+# --no-backups \
+# --monitoring \
+# --ipv6 \
+# --vpc-uuid="$DO_TEST_VPC_UUID"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Droplet provisioned"
+# assert_output_contains "Droplet is active"
+# assert_output_contains "Server added to inventory"
+# assert_command_replay "do:provision"
+#
+# # Tag contract for janitor discovery
+# do_assert_droplet_tag_contract "$DO_TEST_SERVER_NAME"
+# }
+#
+# @test "server:install configures DigitalOcean provisioned server" {
+# require_do_provision_config
+#
+# local primary_php_version secondary_php_version installed_php_versions
+# primary_php_version="$CLOUD_TEST_PHP_PRIMARY_VERSION"
+# secondary_php_version="$CLOUD_TEST_PHP_SECONDARY_VERSION"
+#
+# # Full install takes time - use longer timeout
+# run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
+# --server="$DO_TEST_SERVER_NAME" \
+# --generate-deploy-key \
+# --timezone="UTC" \
+# --php-version="$primary_php_version" \
+# --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Server installation completed"
+# assert_output_contains "public key"
+# assert_command_replay "server:install"
+#
+# # Install secondary PHP-FPM version on the same server (keep primary as default)
+# run timeout 600 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi server:install \
+# --server="$DO_TEST_SERVER_NAME" \
+# --generate-deploy-key \
+# --timezone="UTC" \
+# --php-version="$secondary_php_version" \
+# --no-php-default \
+# --php-extensions="$CLOUD_TEST_PHP_EXTENSIONS"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Server installation completed"
+# assert_command_replay "server:install"
+#
+# installed_php_versions="$(get_installed_php_fpm_versions_for_server "$DO_TEST_SERVER_NAME")"
+# printf '%s\n' "$installed_php_versions" | grep -qx "$primary_php_version"
+# printf '%s\n' "$installed_php_versions" | grep -qx "$secondary_php_version"
+# }
+#
+# # ----
+# # site:create
+# # ----
+#
+# @test "site:create creates site ${DO_TEST_SITE_DOMAIN} on DigitalOcean provisioned server" {
+# require_do_provision_config
+#
+# # Cleanup any leftover test site
+# cleanup_test_site "$DO_TEST_SITE_DOMAIN"
+#
+# run_deployer site:create \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --server="$DO_TEST_SERVER_NAME" \
+# --php-version="$CLOUD_TEST_PHP_PRIMARY_VERSION" \
+# --web-root="/"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "site:create"
+# }
+#
+# @test "site:create creates secondary site ${DO_TEST_SITE_DOMAIN_SECONDARY} on DigitalOcean provisioned server" {
+# require_do_provision_config
+#
+# # Cleanup any leftover secondary test site
+# cleanup_test_site "$DO_TEST_SITE_DOMAIN_SECONDARY"
+#
+# run_deployer site:create \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
+# --server="$DO_TEST_SERVER_NAME" \
+# --php-version="$CLOUD_TEST_PHP_SECONDARY_VERSION" \
+# --web-root="/"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "site:create"
+# }
+#
+# # ----
+# # do:dns:set
+# # ----
+#
+# @test "do:dns:set creates prefixed A record for ${DO_TEST_DNS_ROOT_FQDN}" {
+# require_do_provision_config
+#
+# # Get server IP from inventory
+# local server_ip
+# server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer do:dns:set \
+# --zone="$DO_TEST_DOMAIN" \
+# --type="A" \
+# --name="$DO_TEST_DNS_ROOT" \
+# --value="$server_ip" \
+# --ttl="60"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record"
+# assert_output_contains "successfully"
+# assert_command_replay "do:dns:set"
+# }
+#
+# @test "do:dns:set creates secondary prefixed A record for ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_do_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer do:dns:set \
+# --zone="$DO_TEST_DOMAIN" \
+# --type="A" \
+# --name="$DO_TEST_DNS_ROOT_SECONDARY" \
+# --value="$server_ip" \
+# --ttl="60"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record"
+# assert_output_contains "successfully"
+# assert_command_replay "do:dns:set"
+# }
+#
+# @test "do:dns:list shows ${DO_TEST_DNS_ROOT_FQDN} and ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_do_provision_config
+#
+# run_deployer do:dns:list \
+# --zone="$DO_TEST_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "$DO_TEST_DOMAIN"
+# assert_output_contains "$DO_TEST_DNS_ROOT"
+# assert_output_contains "$DO_TEST_DNS_ROOT_SECONDARY"
+# assert_command_replay "do:dns:list"
+# }
+#
+# # ----
+# # site:dns:check
+# # ----
+#
+# @test "site:dns:check resolves DNS for ${DO_TEST_SITE_DOMAIN}" {
+# require_do_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer site:dns:check \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "Check DNS"
+# assert_output_contains "Domain: $DO_TEST_SITE_DOMAIN"
+# assert_output_contains "A:"
+# assert_output_contains "AAAA:"
+# assert_output_contains "$server_ip"
+# assert_command_replay "site:dns:check"
+# }
+#
+# @test "site:dns:check resolves DNS for ${DO_TEST_SITE_DOMAIN_SECONDARY}" {
+# require_do_provision_config
+#
+# local server_ip
+# server_ip=$(get_server_ip "$DO_TEST_SERVER_NAME")
+#
+# [[ -n "$server_ip" ]] || skip "Could not determine server IP"
+#
+# run_deployer site:dns:check \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "Check DNS"
+# assert_output_contains "Domain: $DO_TEST_SITE_DOMAIN_SECONDARY"
+# assert_output_contains "A:"
+# assert_output_contains "AAAA:"
+# assert_output_contains "$server_ip"
+# assert_command_replay "site:dns:check"
+# }
+#
+# # ----
+# # site:shared:push
+# # ----
+#
+# @test "site:shared:push uploads .env to DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer site:shared:push \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
+# --remote=".env"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file uploaded"
+# assert_command_replay "site:shared:push"
+# }
+#
+# @test "site:shared:push uploads .env to DigitalOcean secondary site" {
+# require_do_provision_config
+#
+# run_deployer site:shared:push \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
+# --local="${BATS_TEST_ROOT}/fixtures/env/deploy-me.env" \
+# --remote=".env"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file uploaded"
+# assert_command_replay "site:shared:push"
+# }
+#
+# # ----
+# # site:shared:list
+# # ----
+#
+# @test "site:shared:list shows uploaded shared files for DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer site:shared:list \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains ".env"
+# assert_command_replay "site:shared:list"
+# }
+#
+# # ----
+# # site:shared:pull
+# # ----
+#
+# @test "site:shared:pull downloads .env from DigitalOcean site" {
+# require_do_provision_config
+#
+# local pulled_env="${BATS_TEST_TMPDIR}/do-shared.env"
+#
+# run_deployer site:shared:pull \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --remote=".env" \
+# --local="$pulled_env" \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Shared file downloaded"
+# assert_command_replay "site:shared:pull"
+# [[ -f "$pulled_env" ]]
+#
+# run grep -q "APP_MESSAGE=$CLOUD_TEST_APP_MESSAGE" "$pulled_env"
+# [ "$status" -eq 0 ]
+# }
+#
+# # ----
+# # site:deploy
+# # ----
+#
+# @test "site:deploy deploys application to DigitalOcean site" {
+# require_do_provision_config
+#
+# # Deploy takes time - use longer timeout
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --repo="$CLOUD_TEST_DEPLOY_REPO" \
+# --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Deployment completed"
+# assert_command_replay "site:deploy"
+# }
+#
+# @test "site:deploy deploys application to DigitalOcean secondary site" {
+# require_do_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:deploy \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
+# --repo="$CLOUD_TEST_DEPLOY_REPO" \
+# --branch="$CLOUD_TEST_DEPLOY_BRANCH" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Deployment completed"
+# assert_command_replay "site:deploy"
+# }
+#
+# # ----
+# # cron/supervisor
+# # ----
+#
+# @test "cron:create adds hello.sh cron for DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer cron:create \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --schedule="* * * * *"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "cron:create"
+# }
+#
+# @test "cron:sync applies hello.sh cron to DigitalOcean server" {
+# require_do_provision_config
+#
+# run_deployer cron:sync \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "synced to server"
+# assert_command_replay "cron:sync"
+# }
+#
+# @test "cron:sync writes DigitalOcean crontab entry and log file for hello.sh" {
+# require_do_provision_config
+#
+# run_deployer server:run \
+# --server="$DO_TEST_SERVER_NAME" \
+# --command="sudo -n crontab -l -u deployer | grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT"
+#
+# run_deployer server:run \
+# --server="$DO_TEST_SERVER_NAME" \
+# --command="test -f /var/log/cron/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT.log && echo cron-log-found"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "cron-log-found"
+# }
+#
+# @test "supervisor:create adds hello.sh program for DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer supervisor:create \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --no-autostart \
+# --no-autorestart \
+# --stopwaitsecs="10" \
+# --numprocs="1"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "added to inventory"
+# assert_command_replay "supervisor:create"
+# }
+#
+# @test "supervisor:sync applies hello.sh program to DigitalOcean server" {
+# require_do_provision_config
+#
+# run_deployer supervisor:sync \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "synced to server"
+# assert_command_replay "supervisor:sync"
+# }
+#
+# @test "supervisor:sync writes DigitalOcean supervisor config for hello.sh" {
+# require_do_provision_config
+#
+# run_deployer server:run \
+# --server="$DO_TEST_SERVER_NAME" \
+# --command="test -f /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && grep -F 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT' /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf && echo supervisor-config-found"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "supervisor-config-found"
+# }
+#
+# @test "supervisor lifecycle commands restart/stop/start work on DigitalOcean server" {
+# require_do_provision_config
+#
+# run_deployer supervisor:restart \
+# --server="$DO_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:restart"
+#
+# run_deployer supervisor:stop \
+# --server="$DO_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:stop"
+#
+# run_deployer supervisor:start \
+# --server="$DO_TEST_SERVER_NAME"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:start"
+# }
+#
+# @test "cron:delete removes hello.sh cron for DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer cron:delete \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --script="$CLOUD_TEST_CRON_SUPERVISOR_SCRIPT" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "cron:delete"
+# }
+#
+# @test "cron:sync removes hello.sh crontab entry from DigitalOcean server" {
+# require_do_provision_config
+#
+# run_deployer cron:sync \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "cron:sync"
+#
+# run_deployer server:run \
+# --server="$DO_TEST_SERVER_NAME" \
+# --command="if sudo -n crontab -l -u deployer 2>/dev/null | grep -Fq 'runner.sh $CLOUD_TEST_CRON_SUPERVISOR_SCRIPT'; then echo cron-entry-present; exit 1; else echo cron-entry-absent; fi"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "cron-entry-absent"
+# }
+#
+# @test "supervisor:delete removes hello.sh program for DigitalOcean site" {
+# require_do_provision_config
+#
+# run_deployer supervisor:delete \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --program="$CLOUD_TEST_SUPERVISOR_PROGRAM" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "supervisor:delete"
+# }
+#
+# @test "supervisor:sync removes hello.sh supervisor config from DigitalOcean server" {
+# require_do_provision_config
+#
+# run_deployer supervisor:sync \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_command_replay "supervisor:sync"
+#
+# run_deployer server:run \
+# --server="$DO_TEST_SERVER_NAME" \
+# --command="if test -f /etc/supervisor/conf.d/$DO_TEST_SITE_DOMAIN-$CLOUD_TEST_SUPERVISOR_PROGRAM.conf; then echo supervisor-config-present; exit 1; else echo supervisor-config-absent; fi"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_output_contains "supervisor-config-absent"
+# }
+#
+# # ----
+# # site:https
+# # ----
+#
+# @test "site:https enables HTTPS for ${DO_TEST_SITE_DOMAIN}" {
+# require_do_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
+# --domain="$DO_TEST_SITE_DOMAIN"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "HTTPS enabled successfully"
+# assert_command_replay "site:https"
+# }
+#
+# @test "site:https enables HTTPS for ${DO_TEST_SITE_DOMAIN_SECONDARY}" {
+# require_do_provision_config
+#
+# run timeout 300 "$DEPLOYER_BIN" --inventory="$TEST_INVENTORY" --no-ansi site:https \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY"
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "HTTPS enabled successfully"
+# assert_command_replay "site:https"
+# }
+#
+# # ----
+# # HTTP Verification
+# # ----
+#
+# @test "deployed DigitalOcean site responds to HTTP requests after HTTPS setup" {
+# require_do_provision_config
+#
+# # Verify app response after HTTPS setup.
+# wait_for_http "$DO_TEST_SITE_DOMAIN" "$CLOUD_TEST_APP_MESSAGE" 30
+# }
+#
+# @test "deployed DigitalOcean secondary site responds to HTTP requests after HTTPS setup" {
+# require_do_provision_config
+#
+# wait_for_http "$DO_TEST_SITE_DOMAIN_SECONDARY" "$CLOUD_TEST_APP_MESSAGE" 30
+# }
+#
+# # ----
+# # site:rollback
+# # ----
+#
+# @test "site:rollback shows forward-only deployment guidance" {
+# require_do_provision_config
+#
+# run_deployer site:rollback
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_info_output
+# assert_output_contains "Forward-only deployments"
+# assert_bullet_output
+# }
+#
+# # ----
+# # site:delete
+# # ----
+#
+# @test "site:delete removes ${DO_TEST_SITE_DOMAIN_SECONDARY} from server and inventory" {
+# require_do_provision_config
+#
+# run_deployer site:delete \
+# --domain="$DO_TEST_SITE_DOMAIN_SECONDARY" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "site:delete"
+# }
+#
+# @test "site:delete removes ${DO_TEST_SITE_DOMAIN} from server and inventory" {
+# require_do_provision_config
+#
+# run_deployer site:delete \
+# --domain="$DO_TEST_SITE_DOMAIN" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "removed from inventory"
+# assert_command_replay "site:delete"
+# }
+#
+# # ----
+# # do:dns:delete
+# # ----
+#
+# @test "do:dns:delete removes prefixed A record ${DO_TEST_DNS_ROOT_SECONDARY_FQDN}" {
+# require_do_provision_config
+#
+# run_deployer do:dns:delete \
+# --zone="$DO_TEST_DOMAIN" \
+# --type="A" \
+# --name="$DO_TEST_DNS_ROOT_SECONDARY" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record deleted successfully"
+# assert_command_replay "do:dns:delete"
+# }
+#
+# @test "do:dns:delete removes prefixed A record ${DO_TEST_DNS_ROOT_FQDN}" {
+# require_do_provision_config
+#
+# run_deployer do:dns:delete \
+# --zone="$DO_TEST_DOMAIN" \
+# --type="A" \
+# --name="$DO_TEST_DNS_ROOT" \
+# --force \
+# --yes
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "DNS record deleted successfully"
+# assert_command_replay "do:dns:delete"
+# }
+#
+# # ----
+# # Cleanup
+# # ----
+#
+# @test "server:delete removes DigitalOcean droplet" {
+# require_do_provision_config
+#
+# run_deployer server:delete \
+# --server="$DO_TEST_SERVER_NAME" \
+# --force \
+# --yes \
+# --destroy-cloud
+#
+# debug_output
+#
+# [ "$status" -eq 0 ]
+# assert_success_output
+# assert_output_contains "Droplet destroyed"
+# assert_output_contains "removed from inventory"
+# assert_command_replay "server:delete"
+# }
From d93cf02082273e5fff5c2057114b148379e82cfa Mon Sep 17 00:00:00 2001
From: Lucian Behind The Scenes
Date: Sun, 9 Aug 2026 13:29:27 +0300
Subject: [PATCH 3/3] ci: disable cloud test jobs and janitor sweeps
Removes the hourly janitor schedule and comments out the cloud-tests
and janitor jobs that depend on retired cloud provider accounts.
---
.github/workflows/ci.yml | 443 ++++++++++++++++++++-------------------
1 file changed, 226 insertions(+), 217 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7e9eb4c..f88fd67 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,8 +24,11 @@ on:
branches:
- main
paths: *ci_paths
- schedule:
- - cron: "0 * * * *"
+ # NOTE: The hourly janitor-sweep schedule is disabled because the cloud
+ # provider accounts it swept (AWS, DigitalOcean, Cloudflare) have been
+ # spun down. Restore it together with the cloud jobs below if needed.
+ # schedule:
+ # - cron: "0 * * * *"
jobs:
trust-context:
@@ -251,218 +254,224 @@ jobs:
BATS_DISTRO: ubuntu24
run: ./bats.sh ci vm ubuntu24
- cloud-tests-do:
- name: cloud-tests (do)
- needs: [trust-context, vm-tests-ubuntu24]
- if: ${{ needs.trust-context.outputs.run_secret_jobs == 'true' && needs.vm-tests-ubuntu24.result == 'success' }}
- runs-on: ubuntu-24.04
- timeout-minutes: 12
- concurrency:
- group: bats-cloud-do-${{ needs.trust-context.outputs.checkout_ref }}
- cancel-in-progress: true
-
- env:
- CI: "true"
- BATS_RUN_SUFFIX: ${{ github.run_id }}
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- with:
- ref: ${{ needs.trust-context.outputs.checkout_ref }}
-
- - name: Setup PHP and Composer
- uses: ./.github/actions/setup-php-composer
-
- - name: Install BATS and dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y bats jq curl
- aws --version
-
- - name: Setup environment file
- run: |
- cat <<'EOF' > .env
- ${{ secrets.DOTENV_FILE }}
- EOF
-
- - name: Setup SSH key for cloud provisioning
- run: |
- mkdir -p ~/.ssh tests/bats/fixtures/keys
- echo "${{ secrets.SSH_PRIVATE_KEY_B64 }}" | base64 -d > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
- ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
- cp ~/.ssh/id_ed25519.pub tests/bats/fixtures/keys/id_test.pub
-
- - name: Run DO tests
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- ./bats.sh ci cloud do
-
- - name: Cleanup cloud resources (backstop)
- if: always()
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- tests/bats/lib/cloud-janitor.sh \
- --mode=targeted \
- --suffix="${BATS_RUN_SUFFIX}" \
- --providers="do"
-
- cloud-tests-aws:
- name: cloud-tests (aws)
- needs: [trust-context, vm-tests-ubuntu24]
- if: ${{ needs.trust-context.outputs.run_secret_jobs == 'true' && needs.vm-tests-ubuntu24.result == 'success' }}
- runs-on: ubuntu-24.04
- timeout-minutes: 12
- concurrency:
- group: bats-cloud-aws-${{ needs.trust-context.outputs.checkout_ref }}
- cancel-in-progress: true
-
- env:
- CI: "true"
- BATS_RUN_SUFFIX: ${{ github.run_id }}
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- with:
- ref: ${{ needs.trust-context.outputs.checkout_ref }}
-
- - name: Setup PHP and Composer
- uses: ./.github/actions/setup-php-composer
-
- - name: Install BATS and dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y bats jq curl
- aws --version
-
- - name: Setup environment file
- run: |
- cat <<'EOF' > .env
- ${{ secrets.DOTENV_FILE }}
- EOF
-
- - name: Setup SSH key for cloud provisioning
- run: |
- mkdir -p ~/.ssh tests/bats/fixtures/keys
- echo "${{ secrets.SSH_PRIVATE_KEY_B64 }}" | base64 -d > ~/.ssh/id_ed25519
- chmod 600 ~/.ssh/id_ed25519
- ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
- cp ~/.ssh/id_ed25519.pub tests/bats/fixtures/keys/id_test.pub
-
- - name: Run AWS tests
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- ./bats.sh ci cloud aws
-
- - name: Cleanup cloud resources (backstop)
- if: always()
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- tests/bats/lib/cloud-janitor.sh \
- --mode=targeted \
- --suffix="${BATS_RUN_SUFFIX}" \
- --providers="aws"
-
- janitor-targeted:
- name: janitor-targeted
- needs: [trust-context, cloud-tests-do, cloud-tests-aws]
- if: >-
- ${{
- always() &&
- needs.trust-context.outputs.run_secret_jobs == 'true' &&
- (needs['cloud-tests-do'].result != 'skipped' || needs['cloud-tests-aws'].result != 'skipped')
- }}
- runs-on: ubuntu-24.04
- timeout-minutes: 12
- concurrency:
- group: bats-cloud-janitor-targeted-${{ github.run_id }}
- cancel-in-progress: false
-
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
-
- - name: Setup PHP and Composer
- uses: ./.github/actions/setup-php-composer
-
- - name: Install janitor dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y jq curl
- aws --version
-
- - name: Setup environment file
- run: |
- cat <<'ENVEOF' > .env
- ${{ secrets.DOTENV_FILE }}
- ENVEOF
-
- - name: Janitor cleanup after cloud jobs
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- tests/bats/lib/cloud-janitor.sh \
- --mode=targeted \
- --suffix="${{ github.run_id }}" \
- --providers="aws,do,cf"
-
- janitor-sweep:
- needs: trust-context
- if: ${{ needs.trust-context.outputs.run_sweep == 'true' }}
- runs-on: ubuntu-24.04
- timeout-minutes: 12
- concurrency:
- group: bats-cloud-janitor
- cancel-in-progress: false
-
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
-
- - name: Setup PHP and Composer
- uses: ./.github/actions/setup-php-composer
-
- - name: Install janitor dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y jq curl
- aws --version
-
- - name: Setup environment file
- run: |
- cat <<'ENVEOF' > .env
- ${{ secrets.DOTENV_FILE }}
- ENVEOF
-
- - name: Janitor scheduled sweep
- run: |
- set -a
- # shellcheck disable=SC1091
- source .env
- set +a
- tests/bats/lib/cloud-janitor.sh \
- --mode=sweep \
- --providers="aws,do,cf" \
- --min-age-minutes=30
+ # ----
+ # NOTE: The jobs below are disabled because the cloud provider accounts
+ # (AWS, DigitalOcean, Cloudflare) and test domains they depend on have
+ # been spun down. Uncomment them (and the schedule trigger above) to
+ # re-enable once active credentials are available again.
+ # ----
+ # cloud-tests-do:
+ # name: cloud-tests (do)
+ # needs: [trust-context, vm-tests-ubuntu24]
+ # if: ${{ needs.trust-context.outputs.run_secret_jobs == 'true' && needs.vm-tests-ubuntu24.result == 'success' }}
+ # runs-on: ubuntu-24.04
+ # timeout-minutes: 12
+ # concurrency:
+ # group: bats-cloud-do-${{ needs.trust-context.outputs.checkout_ref }}
+ # cancel-in-progress: true
+
+ # env:
+ # CI: "true"
+ # BATS_RUN_SUFFIX: ${{ github.run_id }}
+
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ # with:
+ # ref: ${{ needs.trust-context.outputs.checkout_ref }}
+
+ # - name: Setup PHP and Composer
+ # uses: ./.github/actions/setup-php-composer
+
+ # - name: Install BATS and dependencies
+ # run: |
+ # sudo apt-get update
+ # sudo apt-get install -y bats jq curl
+ # aws --version
+
+ # - name: Setup environment file
+ # run: |
+ # cat <<'EOF' > .env
+ # ${{ secrets.DOTENV_FILE }}
+ # EOF
+
+ # - name: Setup SSH key for cloud provisioning
+ # run: |
+ # mkdir -p ~/.ssh tests/bats/fixtures/keys
+ # echo "${{ secrets.SSH_PRIVATE_KEY_B64 }}" | base64 -d > ~/.ssh/id_ed25519
+ # chmod 600 ~/.ssh/id_ed25519
+ # ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
+ # cp ~/.ssh/id_ed25519.pub tests/bats/fixtures/keys/id_test.pub
+
+ # - name: Run DO tests
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # ./bats.sh ci cloud do
+
+ # - name: Cleanup cloud resources (backstop)
+ # if: always()
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # tests/bats/lib/cloud-janitor.sh \
+ # --mode=targeted \
+ # --suffix="${BATS_RUN_SUFFIX}" \
+ # --providers="do"
+
+ # cloud-tests-aws:
+ # name: cloud-tests (aws)
+ # needs: [trust-context, vm-tests-ubuntu24]
+ # if: ${{ needs.trust-context.outputs.run_secret_jobs == 'true' && needs.vm-tests-ubuntu24.result == 'success' }}
+ # runs-on: ubuntu-24.04
+ # timeout-minutes: 12
+ # concurrency:
+ # group: bats-cloud-aws-${{ needs.trust-context.outputs.checkout_ref }}
+ # cancel-in-progress: true
+
+ # env:
+ # CI: "true"
+ # BATS_RUN_SUFFIX: ${{ github.run_id }}
+
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+ # with:
+ # ref: ${{ needs.trust-context.outputs.checkout_ref }}
+
+ # - name: Setup PHP and Composer
+ # uses: ./.github/actions/setup-php-composer
+
+ # - name: Install BATS and dependencies
+ # run: |
+ # sudo apt-get update
+ # sudo apt-get install -y bats jq curl
+ # aws --version
+
+ # - name: Setup environment file
+ # run: |
+ # cat <<'EOF' > .env
+ # ${{ secrets.DOTENV_FILE }}
+ # EOF
+
+ # - name: Setup SSH key for cloud provisioning
+ # run: |
+ # mkdir -p ~/.ssh tests/bats/fixtures/keys
+ # echo "${{ secrets.SSH_PRIVATE_KEY_B64 }}" | base64 -d > ~/.ssh/id_ed25519
+ # chmod 600 ~/.ssh/id_ed25519
+ # ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
+ # cp ~/.ssh/id_ed25519.pub tests/bats/fixtures/keys/id_test.pub
+
+ # - name: Run AWS tests
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # ./bats.sh ci cloud aws
+
+ # - name: Cleanup cloud resources (backstop)
+ # if: always()
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # tests/bats/lib/cloud-janitor.sh \
+ # --mode=targeted \
+ # --suffix="${BATS_RUN_SUFFIX}" \
+ # --providers="aws"
+
+ # janitor-targeted:
+ # name: janitor-targeted
+ # needs: [trust-context, cloud-tests-do, cloud-tests-aws]
+ # if: >-
+ # ${{
+ # always() &&
+ # needs.trust-context.outputs.run_secret_jobs == 'true' &&
+ # (needs['cloud-tests-do'].result != 'skipped' || needs['cloud-tests-aws'].result != 'skipped')
+ # }}
+ # runs-on: ubuntu-24.04
+ # timeout-minutes: 12
+ # concurrency:
+ # group: bats-cloud-janitor-targeted-${{ github.run_id }}
+ # cancel-in-progress: false
+
+ # env:
+ # GITHUB_TOKEN: ${{ github.token }}
+
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+
+ # - name: Setup PHP and Composer
+ # uses: ./.github/actions/setup-php-composer
+
+ # - name: Install janitor dependencies
+ # run: |
+ # sudo apt-get update
+ # sudo apt-get install -y jq curl
+ # aws --version
+
+ # - name: Setup environment file
+ # run: |
+ # cat <<'ENVEOF' > .env
+ # ${{ secrets.DOTENV_FILE }}
+ # ENVEOF
+
+ # - name: Janitor cleanup after cloud jobs
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # tests/bats/lib/cloud-janitor.sh \
+ # --mode=targeted \
+ # --suffix="${{ github.run_id }}" \
+ # --providers="aws,do,cf"
+
+ # janitor-sweep:
+ # needs: trust-context
+ # if: ${{ needs.trust-context.outputs.run_sweep == 'true' }}
+ # runs-on: ubuntu-24.04
+ # timeout-minutes: 12
+ # concurrency:
+ # group: bats-cloud-janitor
+ # cancel-in-progress: false
+
+ # env:
+ # GITHUB_TOKEN: ${{ github.token }}
+
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+
+ # - name: Setup PHP and Composer
+ # uses: ./.github/actions/setup-php-composer
+
+ # - name: Install janitor dependencies
+ # run: |
+ # sudo apt-get update
+ # sudo apt-get install -y jq curl
+ # aws --version
+
+ # - name: Setup environment file
+ # run: |
+ # cat <<'ENVEOF' > .env
+ # ${{ secrets.DOTENV_FILE }}
+ # ENVEOF
+
+ # - name: Janitor scheduled sweep
+ # run: |
+ # set -a
+ # # shellcheck disable=SC1091
+ # source .env
+ # set +a
+ # tests/bats/lib/cloud-janitor.sh \
+ # --mode=sweep \
+ # --providers="aws,do,cf" \
+ # --min-age-minutes=30