Skip to content

Commit fd07a85

Browse files
committed
Update deploy to azure to fix dual environment creation step
1 parent f76a402 commit fd07a85

1 file changed

Lines changed: 126 additions & 158 deletions

File tree

.github/workflows/deploy-to-azure.yml

Lines changed: 126 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -6,170 +6,138 @@ on:
66
- master
77

88
jobs:
9-
# ──────────────────────────────────────────────────────────────────────────
10-
# Job 1: Build and deploy the existing Python / Flask weather app.
11-
# Uses secrets: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUB_ID,
12-
# GHCR_USERNAME, GHCR_PAT, CONTAINER_APP_NAME, RESOURCE_GROUP,
13-
# AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_DEPLOYMENT
14-
# ──────────────────────────────────────────────────────────────────────────
15-
build-and-deploy:
16-
name: Build and Deploy to Azure Container Apps
9+
deploy-container-apps:
10+
name: Build and Deploy Python and Blazor Apps
1711
runs-on: ubuntu-latest
1812
permissions:
19-
id-token: write # Require write permission to Fetch an OIDC token.
13+
id-token: write
2014

21-
steps:
22-
# Checkout the repository
23-
- name: Checkout code
24-
uses: actions/checkout@v3
25-
26-
# Log in to Azure
27-
- name: Log in to Azure
28-
uses: azure/login@v1
29-
with:
30-
client-id: ${{ secrets.AZURE_CLIENT_ID }}
31-
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
32-
subscription-id: ${{ secrets.AZURE_SUB_ID }}
33-
enable-AzPSSession: true
34-
35-
# Build the Docker image
36-
- name: Build Docker image
37-
run: |
38-
docker build -t ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-py:latest .
39-
40-
# Push the Docker image to GitHub Container Registry
41-
- name: Push Docker image to ACR
42-
run: |
43-
echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
44-
docker push ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-py:latest
45-
46-
# Deploy the container to Azure Container Apps
47-
- name: Deploy to Azure Container Apps
48-
run: |
49-
az containerapp update \
50-
--name ${{ secrets.CONTAINER_APP_NAME }} \
51-
--resource-group ${{ secrets.RESOURCE_GROUP }} \
52-
--image ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-py:latest \
53-
--set-env-vars \
54-
"AZURE_OPENAI_API_KEY=${{secrets.AZURE_OPENAI_API_KEY}}" \
55-
"AZURE_OPENAI_ENDPOINT=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
56-
"AZURE_OPENAI_DEPLOYMENT=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
57-
"AZURE_OPENAI_API_VERSION=2024-10-21"
58-
59-
# ──────────────────────────────────────────────────────────────────────────
60-
# Job 2: Build and deploy the Blazor Server weather app.
61-
#
62-
# This job builds the WeatherBlazor project (located in WeatherBlazor/) using
63-
# Dockerfile.blazor and deploys it as a *separate* Azure Container App so that
64-
# Azure assigns it its own unique FQDN/URL.
65-
#
66-
# Required repository secrets (in addition to those shared with Job 1):
67-
# BLAZOR_CONTAINER_APP_NAME – Unique name for the Blazor Container App
68-
# (e.g. "weather-blazor"). Must differ from
69-
# CONTAINER_APP_NAME to receive a distinct URL.
70-
# CONTAINER_APP_ENVIRONMENT – Name of the existing Azure Container Apps
71-
# managed environment (shared with the Python app).
72-
# WEATHER_API_KEY – API key for weatherapi.com used by the Blazor app.
73-
#
74-
# Example: once deployed, the Blazor app will be reachable at a URL such as
75-
# https://weather-blazor.<unique-suffix>.<region>.azurecontainerapps.io
76-
# ──────────────────────────────────────────────────────────────────────────
77-
build-and-deploy-blazor:
78-
name: Build and Deploy Blazor App to Azure Container Apps
79-
runs-on: ubuntu-latest
80-
permissions:
81-
id-token: write # Require write permission to fetch an OIDC token.
15+
env:
16+
RESOURCE_GROUP: ${{ secrets.RESOURCE_GROUP }}
17+
PYTHON_APP_NAME: ${{ secrets.CONTAINER_APP_NAME }}
18+
BLAZOR_APP_NAME: ${{ secrets.BLAZOR_CONTAINER_APP_NAME }}
19+
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
20+
GHCR_PAT: ${{ secrets.GHCR_PAT }}
21+
PYTHON_IMAGE: ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-py:latest
22+
BLAZOR_IMAGE: ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-blazor:latest
8223

8324
steps:
84-
# Checkout the repository so both Dockerfile.blazor and WeatherBlazor/ are available.
85-
- name: Checkout code
86-
uses: actions/checkout@v3
87-
88-
# Log in to Azure using the same OIDC-based federated credentials as the Python job.
89-
- name: Log in to Azure
90-
uses: azure/login@v1
91-
with:
92-
client-id: ${{ secrets.AZURE_CLIENT_ID }}
93-
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
94-
subscription-id: ${{ secrets.AZURE_SUB_ID }}
95-
enable-AzPSSession: true
96-
97-
# Build the Blazor Docker image using the dedicated multi-stage Dockerfile.
98-
# The build context is the repository root so that WeatherBlazor/ is accessible.
99-
- name: Build Blazor Docker image
100-
run: |
101-
docker build \
102-
-f Dockerfile.blazor \
103-
-t ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-blazor:latest \
104-
.
105-
106-
# Authenticate to GHCR and push the Blazor image under a distinct tag
107-
# (weather-blazor) so it does not overwrite the Python app image (weather-py).
108-
- name: Push Blazor Docker image to GHCR
109-
run: |
110-
echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
111-
docker push ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-blazor:latest
112-
113-
# Deploy the Blazor container to a new (or existing) Azure Container App.
114-
#
115-
# Strategy:
116-
# 1. Check whether the Blazor Container App already exists.
117-
# 2. If it does NOT exist, create it inside the same Container Apps environment
118-
# and resource group as the Python app, with external ingress on port 80.
119-
# Azure will assign a unique public URL automatically.
120-
# 3. If it already exists, update the image and environment variables.
121-
#
122-
# ASP.NET Core reads nested config via double-underscore environment variables
123-
# (e.g. AzureOpenAI__ApiKey maps to AzureOpenAI:ApiKey in appsettings.json).
124-
- name: Deploy Blazor app to Azure Container Apps
125-
run: |
126-
BLAZOR_APP="${{ secrets.BLAZOR_CONTAINER_APP_NAME }}"
127-
RESOURCE_GROUP="${{ secrets.RESOURCE_GROUP }}"
128-
IMAGE="ghcr.io/${{ secrets.GHCR_USERNAME }}/weather-blazor:latest"
129-
130-
# Validate that required secrets are present before attempting deployment.
131-
if [ -z "$BLAZOR_APP" ] || [ -z "$RESOURCE_GROUP" ]; then
132-
echo "ERROR: BLAZOR_CONTAINER_APP_NAME and RESOURCE_GROUP secrets must be set."
133-
exit 1
134-
fi
135-
136-
# Check whether the Blazor Container App already exists in the resource group.
137-
APP_EXISTS=$(az containerapp show \
138-
--name "$BLAZOR_APP" \
139-
--resource-group "$RESOURCE_GROUP" \
140-
--query "name" \
141-
--output tsv 2>/dev/null || echo "")
142-
143-
if [ -n "$APP_EXISTS" ]; then
144-
# ── Update path: app exists, refresh image and environment variables. ──
145-
echo "Updating existing Blazor Container App: $BLAZOR_APP"
25+
- name: Checkout code
26+
uses: actions/checkout@v4
27+
28+
- name: Validate deployment configuration
29+
run: |
30+
set -euo pipefail
31+
32+
required_vars=(
33+
RESOURCE_GROUP
34+
PYTHON_APP_NAME
35+
BLAZOR_APP_NAME
36+
GHCR_USERNAME
37+
GHCR_PAT
38+
)
39+
40+
for var_name in "${required_vars[@]}"; do
41+
if [ -z "${!var_name}" ]; then
42+
echo "ERROR: Required workflow value '$var_name' is not configured."
43+
exit 1
44+
fi
45+
done
46+
47+
- name: Log in to Azure
48+
uses: azure/login@v2
49+
with:
50+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
51+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
52+
subscription-id: ${{ secrets.AZURE_SUB_ID }}
53+
54+
- name: Log in to GitHub Container Registry
55+
run: echo "$GHCR_PAT" | docker login ghcr.io -u "$GHCR_USERNAME" --password-stdin
56+
57+
- name: Build Python Docker image
58+
run: docker build -t "$PYTHON_IMAGE" .
59+
60+
- name: Push Python Docker image
61+
run: docker push "$PYTHON_IMAGE"
62+
63+
- name: Deploy Python app to Azure Container Apps
64+
run: |
65+
set -euo pipefail
66+
14667
az containerapp update \
147-
--name "$BLAZOR_APP" \
68+
--name "$PYTHON_APP_NAME" \
14869
--resource-group "$RESOURCE_GROUP" \
149-
--image "$IMAGE" \
70+
--image "$PYTHON_IMAGE" \
15071
--set-env-vars \
151-
"WeatherApiKey=${{ secrets.WEATHER_API_KEY }}" \
152-
"AzureOpenAI__ApiKey=${{ secrets.AZURE_OPENAI_API_KEY }}" \
153-
"AzureOpenAI__Endpoint=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
154-
"AzureOpenAI__Deployment=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
155-
"AzureOpenAI__ApiVersion=2024-10-21"
156-
else
157-
# ── Create path: first-time deployment; provision a new Container App. ──
158-
echo "Creating new Blazor Container App: $BLAZOR_APP"
159-
az containerapp create \
160-
--name "$BLAZOR_APP" \
72+
"AZURE_OPENAI_API_KEY=${{ secrets.AZURE_OPENAI_API_KEY }}" \
73+
"AZURE_OPENAI_ENDPOINT=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
74+
"AZURE_OPENAI_DEPLOYMENT=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
75+
"AZURE_OPENAI_API_VERSION=2024-10-21"
76+
77+
- name: Build Blazor Docker image
78+
run: docker build -f Dockerfile.blazor -t "$BLAZOR_IMAGE" .
79+
80+
- name: Push Blazor Docker image
81+
run: docker push "$BLAZOR_IMAGE"
82+
83+
- name: Resolve existing Container Apps environment
84+
id: container-env
85+
run: |
86+
set -euo pipefail
87+
88+
MANAGED_ENVIRONMENT_ID=$(az containerapp show \
89+
--name "$PYTHON_APP_NAME" \
90+
--resource-group "$RESOURCE_GROUP" \
91+
--query "properties.managedEnvironmentId" \
92+
--output tsv)
93+
94+
if [ -z "$MANAGED_ENVIRONMENT_ID" ]; then
95+
echo "ERROR: Unable to resolve the managed environment from $PYTHON_APP_NAME."
96+
exit 1
97+
fi
98+
99+
echo "managed_environment_id=$MANAGED_ENVIRONMENT_ID" >> "$GITHUB_OUTPUT"
100+
101+
- name: Deploy Blazor app to Azure Container Apps
102+
env:
103+
MANAGED_ENVIRONMENT_ID: ${{ steps.container-env.outputs.managed_environment_id }}
104+
run: |
105+
set -euo pipefail
106+
107+
APP_EXISTS=$(az containerapp show \
108+
--name "$BLAZOR_APP_NAME" \
161109
--resource-group "$RESOURCE_GROUP" \
162-
--environment "${{ secrets.CONTAINER_APP_ENVIRONMENT }}" \
163-
--image "$IMAGE" \
164-
--target-port 80 \
165-
--ingress external \
166-
--registry-server ghcr.io \
167-
--registry-username "${{ secrets.GHCR_USERNAME }}" \
168-
--registry-password "${{ secrets.GHCR_PAT }}" \
169-
--env-vars \
170-
"WeatherApiKey=${{ secrets.WEATHER_API_KEY }}" \
171-
"AzureOpenAI__ApiKey=${{ secrets.AZURE_OPENAI_API_KEY }}" \
172-
"AzureOpenAI__Endpoint=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
173-
"AzureOpenAI__Deployment=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
174-
"AzureOpenAI__ApiVersion=2024-10-21"
175-
fi
110+
--query "name" \
111+
--output tsv 2>/dev/null || true)
112+
113+
if [ -n "$APP_EXISTS" ]; then
114+
echo "Updating existing Blazor Container App: $BLAZOR_APP_NAME"
115+
az containerapp update \
116+
--name "$BLAZOR_APP_NAME" \
117+
--resource-group "$RESOURCE_GROUP" \
118+
--image "$BLAZOR_IMAGE" \
119+
--set-env-vars \
120+
"WeatherApiKey=${{ secrets.WEATHER_API_KEY }}" \
121+
"AzureOpenAI__ApiKey=${{ secrets.AZURE_OPENAI_API_KEY }}" \
122+
"AzureOpenAI__Endpoint=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
123+
"AzureOpenAI__Deployment=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
124+
"AzureOpenAI__ApiVersion=2024-10-21"
125+
else
126+
echo "Creating new Blazor Container App: $BLAZOR_APP_NAME"
127+
az containerapp create \
128+
--name "$BLAZOR_APP_NAME" \
129+
--resource-group "$RESOURCE_GROUP" \
130+
--environment "$MANAGED_ENVIRONMENT_ID" \
131+
--image "$BLAZOR_IMAGE" \
132+
--target-port 80 \
133+
--ingress external \
134+
--registry-server ghcr.io \
135+
--registry-username "$GHCR_USERNAME" \
136+
--registry-password "$GHCR_PAT" \
137+
--env-vars \
138+
"WeatherApiKey=${{ secrets.WEATHER_API_KEY }}" \
139+
"AzureOpenAI__ApiKey=${{ secrets.AZURE_OPENAI_API_KEY }}" \
140+
"AzureOpenAI__Endpoint=${{ secrets.AZURE_OPENAI_ENDPOINT }}" \
141+
"AzureOpenAI__Deployment=${{ secrets.AZURE_OPENAI_DEPLOYMENT }}" \
142+
"AzureOpenAI__ApiVersion=2024-10-21"
143+
fi

0 commit comments

Comments
 (0)