Skip to content

Commit 6b5c578

Browse files
nsheapsclaude
andauthored
Refactor Arcane deployment workflow to support multi-host deployments (#19)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5bd1f64 commit 6b5c578

2 files changed

Lines changed: 129 additions & 8 deletions

File tree

.github/workflows/arcane-deploy.yaml

Lines changed: 126 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,104 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- 'arcane/**'
7+
- 'arcane/hosts/**'
88
- '.github/workflows/arcane-deploy.yaml'
99
workflow_dispatch:
10+
inputs:
11+
force:
12+
description: 'Deploy all stacks regardless of changes (ignore floating tags)'
13+
type: boolean
14+
default: false
1015

1116
concurrency:
1217
group: arcane-deploy
1318
cancel-in-progress: false
1419

1520
jobs:
16-
deploy:
17-
name: Deploy to Arcane
21+
discover:
22+
name: Discover Changed Deployments
1823
runs-on: ubuntu-latest
1924
permissions:
2025
contents: read
26+
outputs:
27+
matrix: ${{ steps.discover.outputs.matrix }}
28+
has-deployments: ${{ steps.discover.outputs.has-deployments }}
2129
steps:
22-
- uses: actions/checkout@v6
30+
- name: Checkout
31+
uses: actions/checkout@v4
32+
with:
33+
fetch-depth: 0
34+
fetch-tags: true
35+
36+
- name: Discover changed deployments
37+
id: discover
38+
env:
39+
FORCE: ${{ inputs.force || 'false' }}
40+
run: |
41+
set -euo pipefail
42+
43+
matrix_items=()
44+
45+
# Find all yaml/yml files at arcane/hosts/{host}/{project}/{file}
46+
# (depth 3 relative to arcane/hosts/)
47+
while IFS= read -r -d '' compose_file; do
48+
rel_path="${compose_file#./}"
49+
50+
# Parse host and project from path: arcane/hosts/{host}/{project}/{file}
51+
host=$(cut -d'/' -f3 <<< "$rel_path")
52+
project=$(cut -d'/' -f4 <<< "$rel_path")
53+
project_dir="arcane/hosts/${host}/${project}"
54+
55+
# Require per-host config file with environment_id
56+
host_config="arcane/hosts/${host}/arcane.json"
57+
if [[ ! -f "$host_config" ]]; then
58+
echo "::warning::No arcane.json found for host '${host}' — skipping '${rel_path}'"
59+
continue
60+
fi
61+
62+
env_id=$(jq -r '.environment_id // empty' "$host_config")
63+
if [[ -z "$env_id" ]]; then
64+
echo "::warning::No environment_id in ${host_config} — skipping '${rel_path}'"
65+
continue
66+
fi
67+
68+
# Floating tag: updated to HEAD after each successful deploy
69+
tag="deployed/arcane/${host}/${project}"
70+
71+
# Skip if nothing in the project directory changed since last deploy
72+
if [[ "${FORCE}" != "true" ]] && git tag -l "$tag" | grep -q .; then
73+
if git diff --quiet "${tag}" HEAD -- "${project_dir}/"; then
74+
echo "No changes in ${project_dir} since ${tag} — skipping"
75+
continue
76+
fi
77+
echo "Changes detected in ${project_dir} since ${tag}"
78+
fi
79+
80+
matrix_items+=("$(jq -cn \
81+
--arg host "$host" \
82+
--arg project "$project" \
83+
--arg compose_file "$rel_path" \
84+
--arg tag "$tag" \
85+
--arg env_id "$env_id" \
86+
'{host: $host, project: $project, "compose-file": $compose_file, tag: $tag, "environment-id": $env_id}')")
87+
done < <(find arcane/hosts -mindepth 3 -maxdepth 3 -type f \
88+
\( -name "*.yaml" -o -name "*.yml" \) -print0 | sort -z)
89+
90+
if [[ ${#matrix_items[@]} -eq 0 ]]; then
91+
echo "No deployments needed"
92+
echo "has-deployments=false" >> "$GITHUB_OUTPUT"
93+
echo 'matrix={"include":[]}' >> "$GITHUB_OUTPUT"
94+
else
95+
matrix_json=$(printf '%s\n' "${matrix_items[@]}" | jq -sc '{include: .}')
96+
echo "has-deployments=true" >> "$GITHUB_OUTPUT"
97+
echo "matrix=${matrix_json}" >> "$GITHUB_OUTPUT"
98+
fi
2399
100+
set-global-vars:
101+
name: Set Arcane Global Variables
102+
runs-on: ubuntu-latest
103+
needs: discover
104+
steps:
24105
- name: Set Arcane global variables
25106
env:
26107
ARCANE_URL: ${{ secrets.ARCANE_URL }}
@@ -50,16 +131,53 @@ jobs:
50131
51132
echo "Global variables updated"
52133
53-
- name: Deploy heapsnas stacks
134+
deploy:
135+
name: Deploy ${{ matrix.host }}/${{ matrix.project }}
136+
runs-on: ubuntu-latest
137+
needs: [discover, set-global-vars]
138+
if: needs.discover.outputs.has-deployments == 'true'
139+
strategy:
140+
matrix: ${{ fromJSON(needs.discover.outputs.matrix) }}
141+
fail-fast: false
142+
permissions:
143+
contents: read
144+
steps:
145+
- name: Checkout
146+
uses: actions/checkout@v4
147+
148+
- name: Deploy to Arcane
54149
# TODO: Pin to commit SHA once nsheaps/github-actions has releases.
55150
# See: https://github.com/nsheaps/iac/issues/3
56151
uses: nsheaps/github-actions/.github/actions/arcane-deploy@main
57152
with:
58153
arcane-url: ${{ secrets.ARCANE_URL }}
59154
arcane-api-key: ${{ secrets.ARCANE_API_KEY }}
60-
environment-id: ${{ secrets.ARCANE_ENVIRONMENT_ID }}
61-
compose-dir: arcane/hosts/heapsnas
62-
sync-name-prefix: heapsnas
155+
environment-id: ${{ matrix['environment-id'] }}
156+
compose-files: ${{ matrix['compose-file'] }}
157+
sync-name-prefix: ${{ matrix.host }}
63158
auth-type: http
64159
git-token: ${{ secrets.GIT_TOKEN }}
65160
branch: main
161+
162+
tag:
163+
name: Tag ${{ matrix.host }}/${{ matrix.project }}
164+
runs-on: ubuntu-latest
165+
needs: [discover, deploy]
166+
if: needs.discover.outputs.has-deployments == 'true'
167+
strategy:
168+
matrix: ${{ fromJSON(needs.discover.outputs.matrix) }}
169+
fail-fast: false
170+
permissions:
171+
contents: write
172+
steps:
173+
- name: Checkout as app
174+
uses: nsheaps/github-actions/.github/actions/checkout-as-app@main
175+
with:
176+
app-id: ${{ secrets.AUTOMATION_GITHUB_APP_ID }}
177+
private-key: ${{ secrets.AUTOMATION_GITHUB_APP_PRIVATE_KEY }}
178+
179+
- name: Push deployment tag
180+
shell: bash
181+
run: |
182+
git tag -f "${{ matrix.tag }}" HEAD
183+
git push origin "refs/tags/${{ matrix.tag }}" --force

arcane/hosts/heapsnas/arcane.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"environment_id": "0"
3+
}

0 commit comments

Comments
 (0)