Skip to content

Commit ee0d45a

Browse files
committed
Unifies multi-image Docker build & deployment
Removes outdated workflows in favor of separate build and deploy steps Adopts OIDC-based Azure login for improved reliability and clarity
1 parent 535253d commit ee0d45a

File tree

3 files changed

+103
-207
lines changed

3 files changed

+103
-207
lines changed

.github/workflows/deploy-azure.yml

Lines changed: 0 additions & 41 deletions
This file was deleted.

.github/workflows/docker-build.yml

Lines changed: 55 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,78 @@
1-
name: Build and Push Docker Image
1+
name: Build and Push Docker Images
22

33
on:
44
push:
55
branches: [ main, develop ]
66
pull_request:
77
branches: [ main ]
8-
# Allow manual trigger
98
workflow_dispatch:
109

1110
env:
1211
REGISTRY: tpsappscriptingacr.azurecr.io
13-
IMAGE_NAME: app-scripting-editor
12+
IMAGE_STATIC: app-scripting-editor
13+
IMAGE_API: app-scripting-editor-api
1414

1515
jobs:
16-
build-and-push:
16+
build:
1717
runs-on: ubuntu-latest
1818
permissions:
1919
contents: read
2020
packages: write
21+
id-token: write
2122

2223
steps:
23-
- name: Checkout repository
24-
uses: actions/checkout@v4
24+
- name: Checkout
25+
uses: actions/checkout@v4
2526

26-
- name: Assert required ACR secrets are set
27-
run: |
28-
if [ -z "${{ secrets.ACR_USERNAME }}" ]; then echo "ERROR: secrets.ACR_USERNAME is not set"; exit 1; fi
29-
if [ -z "${{ secrets.ACR_PASSWORD }}" ]; then echo "ERROR: secrets.ACR_PASSWORD is not set"; exit 1; fi
27+
# --- Login to ACR (simple user/pass; or swap to azure/login + OIDC if you prefer) ---
28+
- name: Docker login to ACR
29+
uses: docker/login-action@v3
30+
with:
31+
registry: ${{ env.REGISTRY }}
32+
username: ${{ secrets.ACR_USERNAME }}
33+
password: ${{ secrets.ACR_PASSWORD }}
3034

31-
- name: Determine registry to use
32-
id: registry
33-
run: |
34-
# Prefer explicit secret ACR_LOGIN_SERVER when present, otherwise use env.REGISTRY
35-
if [ -n "${{ secrets.ACR_LOGIN_SERVER }}" ]; then
36-
echo "registry=${{ secrets.ACR_LOGIN_SERVER }}" >> $GITHUB_OUTPUT
37-
else
38-
echo "registry=${{ env.REGISTRY }}" >> $GITHUB_OUTPUT
39-
fi
35+
- name: Set up QEMU
36+
uses: docker/setup-qemu-action@v3
4037

41-
- name: Set up Docker Buildx
42-
uses: docker/setup-buildx-action@v3
38+
- name: Set up Docker Buildx
39+
uses: docker/setup-buildx-action@v3
4340

44-
- name: Log in to Azure Container Registry
45-
uses: docker/login-action@v3
46-
with:
47-
registry: ${{ steps.registry.outputs.registry }}
48-
username: ${{ secrets.ACR_USERNAME }}
49-
password: ${{ secrets.ACR_PASSWORD }}
41+
# ---------- Build STATIC (nginx) image ----------
42+
# IMPORTANT: set context/file to your actual static image Dockerfile.
43+
# If your static image is built from the repo root with ./Dockerfile.nginx, set file accordingly.
44+
- name: Build & push STATIC image (nginx)
45+
uses: docker/build-push-action@v6
46+
with:
47+
context: .
48+
file: ./Dockerfile.nginx # <--- CHANGE if your static Dockerfile has a different path/name
49+
push: true
50+
tags: |
51+
${{ env.REGISTRY }}/${{ env.IMAGE_STATIC }}:latest
52+
${{ env.REGISTRY }}/${{ env.IMAGE_STATIC }}:${{ github.sha }}
53+
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_STATIC }}:buildcache
54+
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_STATIC }}:buildcache,mode=max
5055

51-
- name: Extract metadata
52-
id: meta
53-
uses: docker/metadata-action@v5
54-
with:
55-
images: ${{ steps.registry.outputs.registry }}/${{ env.IMAGE_NAME }}
56-
tags: |
57-
type=ref,event=branch
58-
type=ref,event=pr
59-
type=sha,prefix={{branch}}-
60-
type=raw,value=latest,enable={{is_default_branch}}
61-
62-
- name: Ensure package-lock.json exists
63-
run: |
64-
if [ ! -f package-lock.json ]; then echo '{}' > package-lock.json; fi
65-
66-
- name: Build and push Docker image
67-
id: build
68-
uses: docker/build-push-action@v5
69-
with:
70-
context: .
71-
file: ./Dockerfile
72-
push: true
73-
tags: ${{ steps.meta.outputs.tags }}
74-
labels: ${{ steps.meta.outputs.labels }}
75-
build-args: |
76-
VITE_APP_VERSION=1.0.0
77-
VITE_APP_BUILD_TIME=${{ github.event.head_commit.timestamp }}
78-
VITE_APP_COMMIT_SHA=${{ github.sha }}
79-
cache-from: type=gha
80-
cache-to: type=gha,mode=max
81-
platforms: linux/amd64,linux/arm64
82-
83-
- name: Image digest
84-
run: echo ${{ steps.build.outputs.digest }}
56+
# ---------- Build API (Node/Express) image ----------
57+
# This Dockerfile copies the built SPA from the STATIC image via:
58+
# ARG REGISTRY
59+
# FROM ${REGISTRY}/app-scripting-editor:latest AS web-build
60+
# COPY --from=web-build /usr/share/nginx/html ./editor-dist
61+
#
62+
# Therefore we MUST pass REGISTRY as a build-arg.
63+
- name: Build & push API image (Node/Express + SPA)
64+
uses: docker/build-push-action@v6
65+
with:
66+
context: .
67+
file: ./Dockerfile # <--- CHANGE if your API Dockerfile has a different path/name
68+
push: true
69+
build-args: |
70+
REGISTRY=${{ env.REGISTRY }}
71+
VITE_APP_VERSION=1.0.0
72+
VITE_APP_BUILD_TIME=${{ github.event.head_commit.timestamp }}
73+
VITE_APP_COMMIT_SHA=${{ github.sha }}
74+
tags: |
75+
${{ env.REGISTRY }}/${{ env.IMAGE_API }}:latest
76+
${{ env.REGISTRY }}/${{ env.IMAGE_API }}:${{ github.sha }}
77+
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_API }}:buildcache
78+
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_API }}:buildcache,mode=max
Lines changed: 48 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,73 @@
1-
name: Build and deploy containers to Azure
1+
name: Deploy API Image to Azure App Service
22

33
on:
4-
push:
4+
workflow_run:
5+
workflows: ["Build and Push Docker Images"]
6+
types: [ completed ]
57
branches: [ main ]
8+
workflow_dispatch:
9+
10+
env:
11+
AZURE_WEBAPP_NAME: tps-app-scripting-editor
12+
AZURE_RESOURCE_GROUP: tps-app-scripting-rg
13+
REGISTRY: tpsappscriptingacr.azurecr.io
14+
IMAGE_API: app-scripting-editor-api
15+
# optional: use your Front Door host for health check; leave blank to use default *.azurewebsites.net
16+
FRONTDOOR_HOST: app-scripting-editor.trackmangolfdev.com
617

718
jobs:
8-
build-and-push:
19+
deploy:
920
runs-on: ubuntu-latest
10-
env:
11-
REGISTRY: tpsappscriptingacr.azurecr.io
12-
outputs:
13-
registry: ${{ steps.registry.outputs.registry }}
14-
steps:
15-
- uses: actions/checkout@v4
16-
17-
- name: Assert required ACR secrets are set
18-
run: |
19-
if [ -z "${{ secrets.ACR_USERNAME }}" ]; then echo "ERROR: secrets.ACR_USERNAME is not set"; exit 1; fi
20-
if [ -z "${{ secrets.ACR_PASSWORD }}" ]; then echo "ERROR: secrets.ACR_PASSWORD is not set"; exit 1; fi
21+
permissions:
22+
id-token: write
23+
contents: read
2124

22-
- name: Determine registry to use
23-
id: registry
24-
run: |
25-
if [ -n "${{ secrets.ACR_LOGIN_SERVER }}" ]; then echo "registry=${{ secrets.ACR_LOGIN_SERVER }}" >> $GITHUB_OUTPUT; else echo "registry=${{ env.REGISTRY }}" >> $GITHUB_OUTPUT; fi
26-
27-
- name: Log in to Azure Container Registry
28-
uses: azure/docker-login@v1
29-
with:
30-
login-server: ${{ steps.registry.outputs.registry }}
31-
username: ${{ secrets.ACR_USERNAME }}
32-
password: ${{ secrets.ACR_PASSWORD }}
33-
34-
- name: Build and push editor image
35-
run: |
36-
docker build \
37-
--build-arg VITE_APP_BUILD_TIME=${{ github.event.head_commit.timestamp }} \
38-
--build-arg VITE_APP_COMMIT_SHA=${{ github.sha }} \
39-
--build-arg VITE_APP_VERSION=1.0.0 \
40-
-t ${{ steps.registry.outputs.registry }}/app-scripting-editor:latest .
41-
docker push ${{ steps.registry.outputs.registry }}/app-scripting-editor:latest
42-
43-
- name: Build and push editor-api image
44-
run: |
45-
docker build \
46-
--build-arg REGISTRY=${{ steps.registry.outputs.registry }} \
47-
--build-arg VITE_APP_BUILD_TIME=${{ github.event.head_commit.timestamp }} \
48-
--build-arg VITE_APP_COMMIT_SHA=${{ github.sha }} \
49-
--build-arg VITE_APP_VERSION=1.0.0 \
50-
-t ${{ steps.registry.outputs.registry }}/app-scripting-editor-api:latest -f server/Dockerfile .
51-
docker push ${{ steps.registry.outputs.registry }}/app-scripting-editor-api:latest
52-
53-
deploy-to-appservice:
54-
needs: build-and-push
55-
runs-on: ubuntu-latest
5625
steps:
57-
- name: Check required App Service secrets
58-
id: check-secrets
59-
run: |
60-
if [ -z "${{ secrets.EDITOR_APP_NAME }}" ] || [ -z "${{ secrets.RESOURCE_GROUP }}" ] || [ -z "${{ secrets.EDITOR_API_APP_NAME }}" ]; then
61-
echo "deploy_ready=false" >> $GITHUB_OUTPUT
62-
echo "One or more App Service secrets are missing; skipping deploy steps."
63-
else
64-
echo "deploy_ready=true" >> $GITHUB_OUTPUT
65-
fi
26+
- name: Checkout
27+
uses: actions/checkout@v4
6628

67-
- name: Azure Login
68-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
69-
uses: azure/login@v1
29+
- name: Azure login (OIDC)
30+
uses: azure/login@v2
7031
with:
71-
creds: ${{ secrets.AZURE_CREDENTIALS }}
32+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
33+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
34+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
7235

73-
- name: Deploy editor to App Service (container)
74-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
36+
- name: Set API image on App Service
7537
run: |
76-
# Deploy the API image to the public editor App Service so the same hostname serves the SPA and the /api endpoints.
77-
az webapp config container set --name ${{ secrets.EDITOR_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --docker-custom-image-name ${{ needs.build-and-push.outputs.registry }}/app-scripting-editor-api:latest
38+
az webapp config container set --name "${{ env.AZURE_WEBAPP_NAME }}" --resource-group "${{ env.AZURE_RESOURCE_GROUP }}" --docker-custom-image-name "${{ env.REGISTRY }}/${{ env.IMAGE_API }}:latest" --docker-registry-server-url "https://${{ env.REGISTRY }}" --docker-registry-server-user "${{ secrets.ACR_USERNAME }}" --docker-registry-server-password "${{ secrets.ACR_PASSWORD }}"
7839
79-
- name: Set WEBSITES_PORT for editor (public) app
80-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
40+
- name: Ensure WEBSITES_PORT=4000 (Express listens here)
8141
run: |
82-
echo "Setting WEBSITES_PORT=4000 for ${{ secrets.EDITOR_APP_NAME }}"
83-
az webapp config appsettings set --name ${{ secrets.EDITOR_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --settings WEBSITES_PORT=4000
42+
az webapp config appsettings set --name "${{ env.AZURE_WEBAPP_NAME }}" --resource-group "${{ env.AZURE_RESOURCE_GROUP }}" --settings WEBSITES_PORT=4000
8443
85-
- name: Deploy editor-api to App Service (container)
86-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
44+
- name: Restart App
8745
run: |
88-
az webapp config container set --name ${{ secrets.EDITOR_API_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --docker-custom-image-name ${{ needs.build-and-push.outputs.registry }}/app-scripting-editor-api:latest
46+
az webapp restart --name "${{ env.AZURE_WEBAPP_NAME }}" --resource-group "${{ env.AZURE_RESOURCE_GROUP }}"
8947
90-
- name: Set WEBSITES_PORT for editor-api
91-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
48+
- name: Determine public host for health check
49+
id: host
9250
run: |
93-
echo "Setting WEBSITES_PORT=4000 for ${EDITOR_API_APP_NAME}"
94-
az webapp config appsettings set --name ${{ secrets.EDITOR_API_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --settings WEBSITES_PORT=4000
95-
96-
- name: Smoke test editor-api
97-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
98-
run: |
99-
host=$(az webapp show --name ${{ secrets.EDITOR_API_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --query defaultHostName -o tsv)
100-
echo "Testing https://$host/api/health"
101-
for i in 1 2 3 4 5 6 7 8 9 10; do
102-
status=$(curl -s -o /dev/null -w "%{http_code}" "https://$host/api/health" || echo "000")
103-
if [ "$status" = "200" ]; then
104-
echo "OK: health returned 200"
105-
exit 0
106-
fi
107-
echo "Attempt $i: status=$status; retrying in 5s..."
108-
sleep 5
109-
done
110-
echo "ERROR: editor-api did not become healthy"
111-
az webapp log tail --name ${{ secrets.EDITOR_API_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} &
112-
exit 1
51+
if [ -n "${{ env.FRONTDOOR_HOST }}" ]; then
52+
echo "host=${{ env.FRONTDOOR_HOST }}" >> "$GITHUB_OUTPUT"
53+
else
54+
host=$(az webapp show --name "${{ env.AZURE_WEBAPP_NAME }}" --resource-group "${{ env.AZURE_RESOURCE_GROUP }}" --query defaultHostName -o tsv)
55+
echo "host=${host}" >> "$GITHUB_OUTPUT"
56+
fi
11357
114-
- name: Smoke test public editor app
115-
if: ${{ steps.check-secrets.outputs.deploy_ready == 'true' }}
58+
- name: Wait for /api/health = 200
11659
run: |
117-
host=$(az webapp show --name ${{ secrets.EDITOR_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} --query defaultHostName -o tsv)
118-
echo "Testing https://$host/api/health"
119-
for i in 1 2 3 4 5 6 7 8 9 10; do
120-
status=$(curl -s -o /dev/null -w "%{http_code}" "https://$host/api/health" || echo "000")
121-
if [ "$status" = "200" ]; then
122-
echo "OK: public editor health returned 200"
60+
set -e
61+
url="https://${{ steps.host.outputs.host }}/api/health"
62+
echo "Checking $url ..."
63+
for i in {1..20}; do
64+
code=$(curl -s -o /dev/null -w "%{http_code}" "$url" || echo "000")
65+
echo "Attempt $i -> $code"
66+
if [ "$code" = "200" ]; then
67+
echo "Healthy."
12368
exit 0
12469
fi
125-
echo "Attempt $i: status=$status; retrying in 5s..."
12670
sleep 5
12771
done
128-
echo "ERROR: public editor did not become healthy"
129-
az webapp log tail --name ${{ secrets.EDITOR_APP_NAME }} --resource-group ${{ secrets.RESOURCE_GROUP }} &
72+
echo "ERROR: health never reached 200"
13073
exit 1

0 commit comments

Comments
 (0)