Skip to content

Associate games with ProfileId instead of PlayerAlias (#270) #576

Associate games with ProfileId instead of PlayerAlias (#270)

Associate games with ProfileId instead of PlayerAlias (#270) #576

Workflow file for this run

name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
AZURE_WEBAPP_NAME: XenobiasoftSudoku-prod
AZURE_API_NAME: XenobiasoftSudokuApi-prod
AZURE_MCP_NAME: XenobiasoftSudokuMcp-prod
AZURE_SWA_NAME: swa-sudoku-xenobiasoft-prod
CUSTOM_DOMAIN_NAME: "sudoku.xenobiasoft.com"
DOTNET_VERSION: "10.x"
permissions:
contents: read
id-token: write
checks: write
pull-requests: write
jobs:
changes:
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
code:
- 'src/**'
- 'infra/**'
- 'Tests/**'
- '**/*.csproj'
- '**/*.sln'
- 'package*.json'
- '.github/workflows/**'
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: "ga"
- name: Cache .NET packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore --verbosity normal
- name: Publish Web Server
run: dotnet publish src/frontend/Sudoku.Blazor/Sudoku.Blazor.csproj --configuration Release --output ${{ github.workspace }}/publish-web --no-restore --no-build
- name: Publish API
run: dotnet publish src/backend/Sudoku.Api/Sudoku.Api.csproj --configuration Release --output ${{ github.workspace }}/publish-api --no-restore --no-build
- name: Publish MCP Server
run: dotnet publish src/backend/Sudoku.McpServer/Sudoku.McpServer.csproj --configuration Release --output ${{ github.workspace }}/publish-mcp --no-restore --no-build
- name: Upload publish-web artifact
uses: actions/upload-artifact@v4
with:
name: publish-web
path: ${{ github.workspace }}/publish-web
- name: Upload publish-api artifact
uses: actions/upload-artifact@v4
with:
name: publish-api
path: ${{ github.workspace }}/publish-api
- name: Upload publish-mcp artifact
uses: actions/upload-artifact@v4
with:
name: publish-mcp
path: ${{ github.workspace }}/publish-mcp
build-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: src/frontend/Sudoku.React/package-lock.json
- name: Install dependencies
run: npm ci
working-directory: src/frontend/Sudoku.React
- name: Run frontend tests
run: npm run test
working-directory: src/frontend/Sudoku.React
- name: Build frontend
run: npm run build
working-directory: src/frontend/Sudoku.React
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: src/frontend/Sudoku.React/dist
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: "ga"
- name: Cache .NET packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore dependencies
run: dotnet restore
- name: Run tests with coverage
run: dotnet test --configuration Release --no-restore --verbosity normal --logger trx --results-directory TestResults --collect:"XPlat Code Coverage" --settings coverlet.runsettings
- name: Generate coverage report
uses: danielpalme/ReportGenerator-GitHub-Action@5
with:
reports: TestResults/**/coverage.cobertura.xml
targetdir: CoverageReport
reporttypes: HtmlInline;Cobertura;MarkdownSummaryGithub
verbosity: Warning
- name: Enforce 80% line coverage threshold
run: |
PERCENT=$(awk 'NR==2 && /line-rate/ { match($0, /line-rate="([0-9.]+)"/, a); printf "%.2f", a[1] * 100 }' CoverageReport/Cobertura.xml)
echo "Line coverage: ${PERCENT}%"
echo "### Coverage Summary" >> $GITHUB_STEP_SUMMARY
echo "Line coverage: **${PERCENT}%**" >> $GITHUB_STEP_SUMMARY
if awk "BEGIN { exit !($PERCENT < 80) }"; then
echo "❌ Coverage ${PERCENT}% is below the required 80% threshold." | tee -a $GITHUB_STEP_SUMMARY
exit 1
else
echo "✅ Coverage ${PERCENT}% meets the required 80% threshold." | tee -a $GITHUB_STEP_SUMMARY
fi
- name: Publish test results as PR annotations
uses: dorny/test-reporter@v1
if: always()
with:
name: .NET Tests
path: TestResults/*.trx
reporter: dotnet-trx
fail-on-error: false
- name: Upload test-results artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: TestResults
deploy-infra:
needs: [test, changes]
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Debug Azure context
run: |
az account show --output table
az account list --output table
az group show -n rg-xenobiasoft-sudoku-prod-westus2 --output table
- name: Deploy Bicep template
id: deploy
uses: azure/arm-deploy@v2
with:
resourceGroupName: rg-xenobiasoft-sudoku-prod-westus2
template: ./infra/main.bicep
parameters: ./infra/params/prod.bicepparam
scope: resourcegroup
failOnStdErr: false
- name: DNS check (scm endpoint)
shell: bash
run: |
sudo apt-get update && sudo apt-get install -y dnsutils
for host in "${AZURE_WEBAPP_NAME}.scm.azurewebsites.net" "${AZURE_API_NAME}.azurewebsites.net" "${CUSTOM_DOMAIN_NAME}"; do
echo "Checking DNS for $host"
for i in {1..10}; do
if nslookup "$host"; then
echo "OK: $host"
break
fi
echo "Waiting for DNS to propagate ($i/10)..."
sleep 15
done
# Do not fail the job just because DNS isn't ready yet
nslookup "$host" || echo "WARNING: $host not resolvable yet; continuing to deploy."
done
- name: Dump deployment error details (if failed)
if: failure()
run: |
echo "Showing deployment operations for 'main'..."
az deployment operation group list \
--resource-group rg-xenobiasoft-sudoku-prod-westus2 \
--name main \
--output jsonc
echo "Showing deployment details for 'main'..."
az deployment group show \
--resource-group rg-xenobiasoft-sudoku-prod-westus2 \
--name main \
--output jsonc
deploy-apps:
needs: deploy-infra
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Download publish-web artifact
uses: actions/download-artifact@v4
with:
name: publish-web
path: ${{ github.workspace }}/publish-web
- name: Download publish-api artifact
uses: actions/download-artifact@v4
with:
name: publish-api
path: ${{ github.workspace }}/publish-api
- name: Deploy Web
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
package: ${{ github.workspace }}/publish-web
- name: Deploy API
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_API_NAME }}
package: ${{ github.workspace }}/publish-api
- name: Download publish-mcp artifact
uses: actions/download-artifact@v4
with:
name: publish-mcp
path: ${{ github.workspace }}/publish-mcp
- name: Deploy MCP Server
uses: azure/webapps-deploy@v3
with:
app-name: ${{ env.AZURE_MCP_NAME }}
package: ${{ github.workspace }}/publish-mcp
deploy-swa:
needs: [deploy-infra, build-frontend, changes]
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
environment: production
steps:
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Get SWA deployment token
id: swa-token
run: |
TOKEN=$(az staticwebapp secrets list \
--name ${{ env.AZURE_SWA_NAME }} \
--query "properties.apiKey" \
--output tsv)
if [ -z "$TOKEN" ]; then
echo "ERROR: Failed to retrieve SWA deployment token for ${{ env.AZURE_SWA_NAME }}" >&2
exit 1
fi
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Download frontend artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: frontend-dist
- name: Deploy to Static Web App
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ steps.swa-token.outputs.token }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "frontend-dist"
output_location: ""
skip_app_build: true