Skip to content

ci: add drupal integration test workflow with issue fork support, for #71 #34

ci: add drupal integration test workflow with issue fork support, for #71

ci: add drupal integration test workflow with issue fork support, for #71 #34

name: Integration Tests
# Creates real workspaces on staging-coder.ddev.com, verifies they start
# correctly, then deletes them. Runs on the self-hosted sysbox runner so
# the Coder provisioner can reach the local Docker/Sysbox environment.
#
# One-time runner setup on staging-coder.ddev.com:
# # Create a dedicated low-privilege user for the runner (do not run as root or admin)
# sudo apt-get install -y unzip # required by actions/checkout and other actions
# sudo useradd -m -s /bin/bash github-runner
#
# # Register N runner instances (one per parallel matrix job — currently 2 templates).
# # TODO: Revisit runner count if more templates or matrix cells are added.
# # Each instance needs its own directory, a unique --name, and its own service.
# # Get a fresh registration token for each from:
# # https://github.com/ddev/coder-ddev/settings/actions/runners/new?arch=x64&os=linux
# # https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/add-runners
# for N in 1 2 3; do
# sudo -u github-runner mkdir -p /home/github-runner/actions-runner-${N}
# cd /home/github-runner/actions-runner-${N}
# # copy runner binaries here (download once, copy to each dir) or re-download
# sudo -u github-runner ./config.sh \
# --url https://github.com/ddev/coder-ddev \
# --token <token> \
# --name staging-coder-${N} \
# --labels sysbox
# sudo ./svc.sh install github-runner # creates actions.runner.*.service
# sudo ./svc.sh start
# done
#
# # To remove/re-register one instance: get removal token from GitHub Settings → Actions → Runners → Remove
# cd /home/github-runner/actions-runner-${N}
# sudo ./svc.sh stop && sudo ./svc.sh uninstall
# sudo -u github-runner ./config.sh remove --token <removal-token>
#
# Requires:
# Repository variable: TEST_CODER_URL - https://staging-coder.ddev.com
# Repository secret: OP_SERVICE_ACCOUNT_TOKEN - 1Password service account with read access
# 1Password item: op://test-secrets/TEST_CODER_SESSION_TOKEN/credential
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run the build with tmate set "debug_enabled"'
type: boolean
required: false
default: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
integration-test:
name: Integration test (${{ matrix.template }})
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
runs-on: [self-hosted, sysbox]
strategy:
matrix:
include:
- template: user-defined-web
extra_vars: ""
extra_params: ""
app_slug: "ddev-web"
- template: freeform
extra_vars: ""
extra_params: ""
app_slug: ""
fail-fast: false
defaults:
run:
shell: bash -euo pipefail {0}
env:
WORKSPACE_NAME: ci-${{ matrix.template }}-${{ github.run_id }}
CI: "true"
DDEV_NONINTERACTIVE: "true"
NO_COLOR: "1"
steps:
- uses: actions/checkout@v6
- name: Load 1Password secrets
uses: 1password/load-secrets-action@v4
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
TEST_CODER_SESSION_TOKEN: "op://test-secrets/TEST_CODER_SESSION_TOKEN/credential"
- name: Login to Coder
if: ${{ env.TEST_CODER_SESSION_TOKEN != '' }}
run: coder login --token "${{ env.TEST_CODER_SESSION_TOKEN }}" "${{ vars.TEST_CODER_URL }}"
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
github-token: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Copy VERSION into template directory
run: cp VERSION ${{ matrix.template }}/VERSION
- name: Push template (inactive)
run: |
coder templates push ${{ matrix.template }} \
--directory ${{ matrix.template }} \
--activate=false \
--name ci-${{ github.run_id }} \
--yes \
--variable workspace_image_registry=index.docker.io/ddev/coder-ddev \
${{ matrix.extra_vars }}
- name: Create workspace
run: |
coder create ${{ env.WORKSPACE_NAME }} \
--template ${{ matrix.template }} \
--template-version ci-${{ github.run_id }} \
--parameter "vscode_extensions=[]" \
${{ matrix.extra_params }} \
--yes
- name: Verify workspace — agent connected
run: coder ssh ${{ env.WORKSPACE_NAME }} --wait=yes -- echo "Agent connected"
- name: Verify workspace — Docker daemon running
run: coder ssh ${{ env.WORKSPACE_NAME }} -- docker ps
- name: Verify workspace — DDEV installed
run: coder ssh ${{ env.WORKSPACE_NAME }} -- ddev --version
- name: Record expected host directory
run: |
OWNER=$(coder whoami --output json | jq -r 'if type == "array" then .[0].username else .username end')
echo "OWNER=$OWNER" >> "$GITHUB_ENV"
echo "HOST_DIR=/coder-workspaces/${OWNER}-${{ env.WORKSPACE_NAME }}" >> "$GITHUB_ENV"
- name: Verify workspace — DDEV can start a project
run: |
# Write start script to runner-local file so we avoid the coder ssh heredoc+PTY hang
cat > /tmp/ci-ddev-start-${{ github.run_id }}.sh << 'EOF'
set -euo pipefail
TESTDIR=/tmp/ci-ddev-${{ github.run_id }}
echo "--- Creating test project in $TESTDIR ---"
mkdir -p "$TESTDIR/web" && cd "$TESTDIR"
ddev config --project-type=php --docroot=web
echo "--- Starting DDEV project ---"
ddev start -y
echo "--- DDEV started ---"
EOF
scp \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ProxyCommand="coder ssh --stdio ${{ env.WORKSPACE_NAME }}" \
/tmp/ci-ddev-start-${{ github.run_id }}.sh \
coder@workspace:/tmp/ci-ddev-start-${{ github.run_id }}.sh
coder ssh ${{ env.WORKSPACE_NAME }} -- \
env CI=${{ env.CI }} DDEV_NONINTERACTIVE=${{ env.DDEV_NONINTERACTIVE }} NO_COLOR=${{ env.NO_COLOR }} \
bash /tmp/ci-ddev-start-${{ github.run_id }}.sh < /dev/null
- name: Verify workspace — DDEV web externally accessible
if: ${{ matrix.app_slug != '' }}
run: |
CODER_DOMAIN="${{ vars.TEST_CODER_URL }}"
CODER_DOMAIN="${CODER_DOMAIN#https://}"
SITE_URL="https://${{ matrix.app_slug }}--${{ env.WORKSPACE_NAME }}--${OWNER}.${CODER_DOMAIN}"
echo "Checking $SITE_URL"
STATUS=$(curl -s --max-time 30 -o /dev/null -w "%{http_code}" "$SITE_URL")
echo "HTTP status: $STATUS"
[[ "$STATUS" =~ ^[2-4] ]] || { echo "ERROR: unexpected HTTP status $STATUS" >&2; exit 1; }
- name: Cleanup DDEV test project
run: |
coder ssh ${{ env.WORKSPACE_NAME }} -- \
env CI=${{ env.CI }} DDEV_NONINTERACTIVE=${{ env.DDEV_NONINTERACTIVE }} NO_COLOR=${{ env.NO_COLOR }} \
bash -c "cd /tmp/ci-ddev-${{ github.run_id }} && ddev delete --omit-snapshot -y && rm -rf /tmp/ci-ddev-${{ github.run_id }}" \
< /dev/null
- name: Delete workspace
if: always()
run: coder delete ${{ env.WORKSPACE_NAME }} --yes || true
- name: Verify host directory removed
if: always()
run: |
if [[ -z "${HOST_DIR:-}" ]]; then
echo "HOST_DIR not set — workspace may not have been created, skipping"
exit 0
fi
if [[ -d "$HOST_DIR" ]]; then
echo "ERROR: Host directory was not removed by destroy provisioner: $HOST_DIR" >&2
ls -la "$HOST_DIR" >&2 || true
exit 1
fi
echo "OK: host directory removed: $HOST_DIR"
- name: Archive CI template version
if: always()
run: coder templates versions archive ${{ matrix.template }} ci-${{ github.run_id }} --yes || true