Skip to content

Merge pull request #35 from danrayson/feature/22-chatbot #28

Merge pull request #35 from danrayson/feature/22-chatbot

Merge pull request #35 from danrayson/feature/22-chatbot #28

name: Deploy Staging
on:
push:
branches: [develop]
workflow_dispatch:
inputs:
imageTag:
description: 'Docker image tag to deploy'
required: false
default: 'latest'
env:
AZURE_RESOURCE_GROUP: rg-raysoncv-staging
LOCATION: uksouth
ACR_NAME: acrraysoncvstaging
API_IMAGE_NAME: raysoncv-api
UI_IMAGE_NAME: raysoncv-ui
OLLAMA_IMAGE_NAME: raysoncv-ollama
jobs:
build:
runs-on: ubuntu-latest
outputs:
imageTag: ${{ steps.set-tag.outputs.imageTag }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
cache-dependency-path: Rayson.CV/UI/package-lock.json
- name: Build API
run: dotnet build Rayson.CV/Api/Presentation/Presentation.csproj --configuration Release
- name: Install UI dependencies
run: cd Rayson.CV/UI && npm ci
- name: Build UI
run: cd Rayson.CV/UI && npm run build
- name: Set image tag
id: set-tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.imageTag }}" ]; then
echo "imageTag=${{ github.event.inputs.imageTag }}" >> $GITHUB_OUTPUT
else
echo "imageTag=${{ github.sha }}" >> $GITHUB_OUTPUT
fi
deploy-core:
runs-on: ubuntu-latest
needs: build
outputs:
acrLoginServer: ${{ steps.deploy.outputs.acrLoginServer }}
acrName: ${{ steps.deploy.outputs.acrName }}
environmentId: ${{ steps.deploy.outputs.environmentId }}
defaultDomain: ${{ steps.deploy.outputs.defaultDomain }}
storageAccountName: ${{ steps.deploy.outputs.storageAccountName }}
blobBaseUrl: ${{ steps.deploy.outputs.blobBaseUrl }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy core infrastructure
id: deploy
run: |
az deployment sub create \
--name raysoncv-staging-core \
--location ${{ env.LOCATION }} \
--template-file infra/main-core.bicep \
--parameters location=${{ env.LOCATION }} \
resourceGroupName=${{ env.AZURE_RESOURCE_GROUP }} \
environmentName=staging \
acrName=${{ env.ACR_NAME }}
ACR_LOGIN_SERVER=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.acrLoginServer.value --output tsv)
ACR_NAME_OUT=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.acrName.value --output tsv)
ENVIRONMENT_ID=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.environmentId.value --output tsv)
DEFAULT_DOMAIN=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.defaultDomain.value --output tsv)
STORAGE_ACCOUNT_NAME=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.storageAccountName.value --output tsv)
BLOB_BASE_URL=$(az deployment sub show --name raysoncv-staging-core --query properties.outputs.blobBaseUrl.value --output tsv)
echo "acrLoginServer=$ACR_LOGIN_SERVER" >> $GITHUB_OUTPUT
echo "acrName=$ACR_NAME_OUT" >> $GITHUB_OUTPUT
echo "environmentId=$ENVIRONMENT_ID" >> $GITHUB_OUTPUT
echo "defaultDomain=$DEFAULT_DOMAIN" >> $GITHUB_OUTPUT
echo "storageAccountName=$STORAGE_ACCOUNT_NAME" >> $GITHUB_OUTPUT
echo "blobBaseUrl=$BLOB_BASE_URL" >> $GITHUB_OUTPUT
push:
runs-on: ubuntu-latest
needs: [build, deploy-core]
outputs:
apiFqdn: ${{ steps.set-fqdn.outputs.apiFqdn }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Login to Azure Container Registry
run: az acr login --name ${{ env.ACR_NAME }}
- name: Build and push API image
run: |
docker build \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.API_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }} \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.API_IMAGE_NAME }}:latest \
./Rayson.CV/Api
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.API_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }}
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.API_IMAGE_NAME }}:latest
- name: Build and push UI image
run: |
API_FQDN="ca-api-staging.${{ needs.deploy-core.outputs.defaultDomain }}"
BLOB_URL="${{ needs.deploy-core.outputs.blobBaseUrl }}"
docker build \
--build-arg VITE_API_BASE_URL=https://$API_FQDN/ \
--build-arg VITE_APP_DOWNLOAD_URL=$BLOB_URL \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.UI_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }} \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.UI_IMAGE_NAME }}:latest \
./Rayson.CV/UI
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.UI_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }}
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.UI_IMAGE_NAME }}:latest
echo "apiFqdn=$API_FQDN" >> $GITHUB_OUTPUT
- name: Build and push Ollama image
run: |
docker build \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.OLLAMA_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }} \
-t ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.OLLAMA_IMAGE_NAME }}:latest \
-f Rayson.CV/ollama.Dockerfile \
Rayson.CV
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.OLLAMA_IMAGE_NAME }}:${{ needs.build.outputs.imageTag }}
docker push ${{ needs.deploy-core.outputs.acrLoginServer }}/${{ env.OLLAMA_IMAGE_NAME }}:latest
- name: Set FQDN output
id: set-fqdn
run: |
echo "apiFqdn=ca-api-staging.${{ needs.deploy-core.outputs.defaultDomain }}" >> $GITHUB_OUTPUT
build-electron-linux:
runs-on: ubuntu-latest
needs: [build, deploy-core]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
cache-dependency-path: Rayson.CV/UI/package-lock.json
- name: Install dependencies
run: cd Rayson.CV/UI && npm ci
- name: Build Electron app
run: cd Rayson.CV/UI && npm run electron:build -- --linux
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: electron-linux
path: Rayson.CV/UI/build/*.AppImage
build-electron-mac:
runs-on: macos-latest
needs: [build, deploy-core]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
cache-dependency-path: Rayson.CV/UI/package-lock.json
- name: Install dependencies
run: cd Rayson.CV/UI && npm ci
- name: Build Electron app
run: cd Rayson.CV/UI && npm run electron:build -- --mac
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: electron-mac
path: Rayson.CV/UI/build/*.dmg
build-electron-windows:
runs-on: windows-latest
needs: [build, deploy-core]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
cache-dependency-path: Rayson.CV/UI/package-lock.json
- name: Install dependencies
run: cd Rayson.CV/UI && npm ci
- name: Build Electron app
run: cd Rayson.CV/UI && npm run electron:build -- --win
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: electron-windows
path: Rayson.CV/UI/build/*.exe
upload-electron:
runs-on: ubuntu-latest
needs: [build-electron-linux, build-electron-mac, build-electron-windows, deploy-core]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: List downloaded artifacts
run: |
echo "=== Linux ==="
find artifacts/electron-linux -type f -exec ls -lh {} \;
echo "=== macOS ==="
find artifacts/electron-mac -type f -exec ls -lh {} \;
echo "=== Windows ==="
find artifacts/electron-windows -type f -exec ls -lh {} \;
- name: Upload Linux app
run: |
FILE=$(ls artifacts/electron-linux/*.AppImage | head -1)
echo "Uploading Linux: $FILE"
az storage blob upload \
--container-name '$web' \
--file "$FILE" \
--name "RaysonCV.AppImage" \
--connection-string "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}" \
--overwrite
- name: Upload macOS app
run: |
FILE=$(ls artifacts/electron-mac/*.dmg | head -1)
echo "Uploading macOS: $FILE"
az storage blob upload \
--container-name '$web' \
--file "$FILE" \
--name "RaysonCV.dmg" \
--connection-string "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}" \
--overwrite
- name: Upload Windows app
run: |
FILE=$(ls artifacts/electron-windows/*.exe | head -1)
echo "Uploading Windows: $FILE"
az storage blob upload \
--container-name '$web' \
--file "$FILE" \
--name "RaysonCV-Setup.exe" \
--connection-string "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}" \
--overwrite
deploy-apps:
runs-on: ubuntu-latest
needs: [build, deploy-core, push]
outputs:
apiFqdn: ${{ steps.deploy.outputs.apiFqdn }}
uiFqdn: ${{ steps.deploy.outputs.uiFqdn }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy container apps
id: deploy
run: |
az deployment group create \
--name raysoncv-staging-apps \
--resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
--template-file infra/main-apps.bicep \
--parameters location=${{ env.LOCATION }} \
environmentId=${{ needs.deploy-core.outputs.environmentId }} \
defaultDomain=${{ needs.deploy-core.outputs.defaultDomain }} \
acrLoginServer=${{ needs.deploy-core.outputs.acrLoginServer }} \
acrName=${{ needs.deploy-core.outputs.acrName }} \
imageTag=${{ needs.build.outputs.imageTag }} \
environmentName=staging \
storageAccountName="${{ needs.deploy-core.outputs.storageAccountName }}" \
blobBaseUrl="${{ needs.deploy-core.outputs.blobBaseUrl }}"
API_FQDN=$(az deployment group show --name raysoncv-staging-apps --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --query properties.outputs.apiFqdn.value --output tsv)
UI_FQDN=$(az deployment group show --name raysoncv-staging-apps --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --query properties.outputs.uiFqdn.value --output tsv)
echo "apiFqdn=$API_FQDN" >> $GITHUB_OUTPUT
echo "uiFqdn=$UI_FQDN" >> $GITHUB_OUTPUT
- name: Deployment Summary
run: |
echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Service | URL |" >> $GITHUB_STEP_SUMMARY
echo "|---------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| API | https://${{ steps.deploy.outputs.apiFqdn }} |" >> $GITHUB_STEP_SUMMARY
echo "| UI | https://${{ steps.deploy.outputs.uiFqdn }} |" >> $GITHUB_STEP_SUMMARY
e2e-tests:
runs-on: ubuntu-latest
needs: deploy-apps
if: github.event_name == 'push'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: cd Rayson.CV/Test/e2e && npm ci
- name: Install Playwright browsers
run: npx playwright install chromium
- name: Run E2E tests
working-directory: Rayson.CV/Test/e2e
run: npm run e2e:staging
env:
E2E_API_URL: https://${{ needs.deploy-apps.outputs.apiFqdn }}
E2E_UI_URL: https://${{ needs.deploy-apps.outputs.uiFqdn }}
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-report
path: Rayson.CV/Test/e2e/reports/