Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions .github/workflows/deploy-ansible.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,23 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

# az runs in the pinned azure/cli container to avoid the Python 3.14
# command-module deadlock in the runner's bundled CLI (Azure/azure-cli#32980).
- name: Resolve VM public IP
run: |
IP=$(az vm show -d \
--resource-group "$RESOURCE_GROUP" \
--name "$VM_NAME" \
--query publicIps -o tsv)
if [ -z "$IP" ]; then
echo "::error::VM has no public IP. Run 'Start environment' first."
exit 1
fi
echo "VM_IP=$IP" >> "$GITHUB_ENV"
id: ip
uses: azure/cli@v2
with:
azcliversion: "2.64.0"
inlineScript: |
IP=$(az vm show -d \
--resource-group "$RESOURCE_GROUP" \
--name "$VM_NAME" \
--query publicIps -o tsv)
if [ -z "$IP" ]; then
echo "::error::VM has no public IP. Run 'Start environment' first."
exit 1
fi
echo "ip=$IP" >> "$GITHUB_OUTPUT"

- name: Set up Python
uses: actions/setup-python@v5
Expand All @@ -64,9 +70,13 @@ jobs:
known_hosts: 'placeholder' # Will be overwritten by ssh-keyscan next

- name: Scan VM Host Key
env:
VM_IP: ${{ steps.ip.outputs.ip }}
run: ssh-keyscan -H "$VM_IP" >> ~/.ssh/known_hosts

- name: Write inventory with the resolved IP
env:
VM_IP: ${{ steps.ip.outputs.ip }}
run: |
cat > infra/inventory.ini <<EOF
[web]
Expand Down
80 changes: 36 additions & 44 deletions .github/workflows/vm-start.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ name: Start environment
# starts the VM (containers auto-restart via `restart: unless-stopped`), and
# arms an auto-shutdown timer so the VM deallocates itself after `minutes`.
# The public URL is printed in the run summary.
#
# az runs inside the pinned azure/cli container: the runner's bundled CLI is on
# Python 3.14, which deadlocks on concurrent command-module loading
# (Azure/azure-cli#32980). The container uses a known-good CLI/Python.

on:
workflow_dispatch:
Expand Down Expand Up @@ -36,52 +40,40 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Create public IP (if missing)
run: |
az network public-ip create \
--resource-group "$RESOURCE_GROUP" \
--name "$PIP_NAME" \
--location "$LOCATION" \
--sku Standard \
--allocation-method Static \
--only-show-errors

- name: Attach public IP to the VM's NIC
run: |
az network nic ip-config update \
--resource-group "$RESOURCE_GROUP" \
--nic-name "$NIC_NAME" \
--name "$IP_CONFIG" \
--public-ip-address "$PIP_NAME" \
--only-show-errors

- name: Start the VM
run: az vm start --resource-group "$RESOURCE_GROUP" --name "$VM_NAME"

- name: Arm auto-shutdown timer (now + minutes, UTC)
- name: Compute auto-shutdown time (UTC)
id: t
env:
MINUTES: ${{ github.event.inputs.minutes }}
run: |
SHUTDOWN=$(date -u -d "+${MINUTES} minutes" +%H%M)
az vm auto-shutdown \
--resource-group "$RESOURCE_GROUP" \
--name "$VM_NAME" \
--time "$SHUTDOWN"
echo "SHUTDOWN_UTC=$SHUTDOWN" >> "$GITHUB_ENV"
run: echo "shutdown=$(date -u -d "+${MINUTES} minutes" +%H%M)" >> "$GITHUB_OUTPUT"

- name: Publish the URL
- name: Start the environment
uses: azure/cli@v2
env:
SHUTDOWN: ${{ steps.t.outputs.shutdown }}
MINUTES: ${{ github.event.inputs.minutes }}
run: |
IP=$(az network public-ip show \
--resource-group "$RESOURCE_GROUP" \
--name "$PIP_NAME" \
--query ipAddress -o tsv)
{
echo "### 🚀 Environment starting"
echo ""
echo "**URL:** http://$IP (allow ~1–2 min for containers to come up)"
echo ""
echo "Auto-shutdown armed for **${SHUTDOWN_UTC} UTC** (in ${MINUTES} min)."
echo "Run **Stop environment** to end early and release the IP."
} >> "$GITHUB_STEP_SUMMARY"
with:
azcliversion: "2.64.0"
inlineScript: |
set -e
az network public-ip create \
--resource-group "$RESOURCE_GROUP" --name "$PIP_NAME" \
--location "$LOCATION" --sku Standard --allocation-method Static \
--only-show-errors
az network nic ip-config update \
--resource-group "$RESOURCE_GROUP" --nic-name "$NIC_NAME" \
--name "$IP_CONFIG" --public-ip-address "$PIP_NAME" --only-show-errors
az vm start --resource-group "$RESOURCE_GROUP" --name "$VM_NAME"
az vm auto-shutdown \
--resource-group "$RESOURCE_GROUP" --name "$VM_NAME" --time "$SHUTDOWN"
IP=$(az network public-ip show \
--resource-group "$RESOURCE_GROUP" --name "$PIP_NAME" \
--query ipAddress -o tsv)
echo "Environment URL: http://$IP (auto-shutdown ${SHUTDOWN} UTC, in ${MINUTES} min)"
{
echo "### 🚀 Environment starting"
echo ""
echo "**URL:** http://$IP (allow ~1–2 min for containers to come up)"
echo ""
echo "Auto-shutdown armed for **${SHUTDOWN} UTC** (in ${MINUTES} min)."
echo "Run **Stop environment** to end early and release the IP."
} >> "$GITHUB_STEP_SUMMARY"
42 changes: 19 additions & 23 deletions .github/workflows/vm-stop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name: Stop environment
# Deallocates the VM (stops compute billing) and deletes the public IP, so the
# only ongoing cost while idle is the OS disk. Safe to run anytime; the VM's
# data and containers survive on the disk and return on the next start.
#
# az runs inside the pinned azure/cli container to avoid the Python 3.14
# command-module deadlock in the runner's bundled CLI (Azure/azure-cli#32980).

on:
workflow_dispatch:
Expand All @@ -29,26 +32,19 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Deallocate the VM
run: az vm deallocate --resource-group "$RESOURCE_GROUP" --name "$VM_NAME"

- name: Detach and delete the public IP
run: |
az network nic ip-config update \
--resource-group "$RESOURCE_GROUP" \
--nic-name "$NIC_NAME" \
--name "$IP_CONFIG" \
--remove publicIpAddress \
--only-show-errors || true
az network public-ip delete \
--resource-group "$RESOURCE_GROUP" \
--name "$PIP_NAME" \
--only-show-errors || true

- name: Summary
run: |
{
echo "### 🛑 Environment stopped"
echo ""
echo "VM deallocated and public IP released."
} >> "$GITHUB_STEP_SUMMARY"
- name: Stop the environment
uses: azure/cli@v2
with:
azcliversion: "2.64.0"
inlineScript: |
az vm deallocate --resource-group "$RESOURCE_GROUP" --name "$VM_NAME"
az network nic ip-config update \
--resource-group "$RESOURCE_GROUP" --nic-name "$NIC_NAME" \
--name "$IP_CONFIG" --remove publicIpAddress --only-show-errors || true
az network public-ip delete \
--resource-group "$RESOURCE_GROUP" --name "$PIP_NAME" --only-show-errors || true
{
echo "### 🛑 Environment stopped"
echo ""
echo "VM deallocated and public IP released. Idle cost is now disk-only (~€0.04/day)."
} >> "$GITHUB_STEP_SUMMARY"
Loading