Skip to content

Consume .NET daily builds #4497

Consume .NET daily builds

Consume .NET daily builds #4497

Workflow file for this run

name: build
on:
push:
branches: [ main ]
paths-ignore:
- '**/*.gitattributes'
- '**/*.gitignore'
- '**/*.md'
pull_request:
branches:
- main
- dotnet-vnext
- dotnet-nightly
workflow_dispatch:
env:
AWS_CLOUDFORMATION_STACK: adventofcode
AWS_REGION: eu-west-2
AWS_URL_PROD: https://aoc.martincostello.com
AZURE_URL_PROD: https://adventofcode-martincostello.azurewebsites.net
AZURE_WEBAPP_NAME: adventofcode-martincostello
CONTAINER_REGISTRY: '${{ github.repository_owner }}.azurecr.io'
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_GENERATE_ASPNET_CERTIFICATE: false
DOTNET_NOLOGO: true
DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1
FORCE_COLOR: 1
NUGET_XMLDOC_MODE: skip
TERM: xterm
permissions:
contents: read
jobs:
build:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
env:
PUBLISH_CONTAINER: ${{ github.event.repository.fork == false && github.ref_name == github.event.repository.default_branch && matrix.os == 'ubuntu-latest' }}
outputs:
container-tag: ${{ steps.publish-container.outputs.container-tag }}
lambda-tools-version: ${{ steps.get-lambda-tools-version.outputs.lambda-tools-version }}
permissions:
attestations: write
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get npm cache directory
id: npm-cache-dir
shell: pwsh
run: echo "dir=$(npm config get cache)" >> ${env:GITHUB_OUTPUT}
- name: Setup npm cache
uses: actions/cache@v4
id: npm-cache
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-node-
- name: Setup NuGet cache
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }}
restore-keys: ${{ runner.os }}-nuget-
- name: Setup Playwright cache
uses: actions/cache@v4
with:
key: ${{ runner.os }}-playwright-${{ hashFiles('Directory.Packages.props') }}
path: |
~/AppData/Local/ms-playwright
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
- name: Install .NET tools
shell: pwsh
run: dotnet tool restore
- name: Build, test and publish
id: build
shell: pwsh
run: ./build.ps1
- name: Publish lambda
uses: actions/upload-artifact@v4
if: runner.os == 'Linux' && success()
with:
name: lambda
path: ./artifacts/publish/lambda.zip
if-no-files-found: error
- name: Docker log in
uses: docker/login-action@v3
if: env.PUBLISH_CONTAINER == 'true'
with:
registry: ${{ env.CONTAINER_REGISTRY }}
username: ${{ secrets.ACR_REGISTRY_USERNAME }}
password: ${{ secrets.ACR_REGISTRY_PASSWORD }}
- name: Publish container
id: publish-container
if: runner.os == 'Linux'
shell: pwsh
env:
ContainerRegistry: ${{ env.PUBLISH_CONTAINER == 'true' && env.CONTAINER_REGISTRY || '' }}
run: |
dotnet publish ./src/AdventOfCode.Site --arch x64 --os linux -p:PublishProfile=DefaultContainer
- name: Attest container image
uses: actions/attest-build-provenance@v1
if: steps.publish-container.outputs.container-digest != ''
with:
push-to-registry: true
subject-digest: ${{ steps.publish-container.outputs.container-digest }}
subject-name: ${{ steps.publish-container.outputs.container-image }}
- name: Publish screenshots
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: screenshots-${{ matrix.os }}
path: ./artifacts/screenshots/*.png
if-no-files-found: ignore
- name: Publish traces
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: traces-${{ matrix.os }}
path: ./artifacts/traces/*
if-no-files-found: ignore
- name: Publish videos
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: videos-${{ matrix.os }}
path: ./artifacts/videos/*
if-no-files-found: ignore
- name: Get Amazon Lambda tools version
id: get-lambda-tools-version
shell: pwsh
run: |
$lambdaToolsVersion = (Get-Content "./.config/dotnet-tools.json" | Out-String | ConvertFrom-Json).tools.'amazon.lambda.tools'.version
"lambda-tools-version=${lambdaToolsVersion}" >> $env:GITHUB_OUTPUT
- name: Upload any crash dumps
shell: pwsh
if: |
!cancelled() &&
steps.build.outcome == 'failure' &&
github.event.repository.fork == false &&
github.event.sender.login != 'dependabot[bot]'
env:
AZURE_STORAGE_CONNECTION_STRING: ${{ secrets.CRASH_DUMPS_STORAGE_CONNECTION_STRING }}
PSCOMPRESSION_VERSION: '2.0.6'
run: |
$dumps = Get-ChildItem -Path ${env:GITHUB_WORKSPACE} -Filter "*.dmp" -Recurse
if ($null -ne $dumps) {
$container = ${env:GITHUB_REPOSITORY}.Replace("/", "-")
az storage container create --name $container --public-access off | Out-Null
Install-Module PSCompression -RequiredVersion ${env:PSCOMPRESSION_VERSION} -AcceptLicense -Force -Scope CurrentUser
$dumps | ForEach-Object {
$zipPath = $_.FullName + ".zip"
$zipName = $_.Name + ".zip"
Write-Output "Compressing crash dump $($_.Name)..."
Compress-ZipArchive -Path $_.FullName -Destination $zipPath
az storage blob upload `
--container-name $container `
--file $zipPath `
--name $zipName `
--metadata "GITHUB_RUN_ATTEMPT=${env:GITHUB_RUN_ATTEMPT}" "GITHUB_WORKFLOW=${env:GITHUB_SERVER_URL}/${env:GITHUB_REPOSITORY}/actions/runs/${env:GITHUB_RUN_ID}" "RUNNER_OS=${env:RUNNER_OS}" `
--overwrite true
if ($LASTEXITCODE -eq 0) {
Write-Output "::notice::Uploaded crash dump $($_.Name) to Azure Storage."
}
}
}
typescript:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get npm cache directory
id: npm-cache-dir
shell: pwsh
run: echo "dir=$(npm config get cache)" >> ${env:GITHUB_OUTPUT}
- name: Setup npm cache
uses: actions/cache@v4
id: npm-cache
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-node-
- name: Build and test
shell: pwsh
working-directory: ./src/AdventOfCode.Site
run: |
npm ci
npm run all
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./src/AdventOfCode.Site/coverage/lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
deploy:
if: github.event.repository.fork == false && github.ref_name == github.event.repository.default_branch
name: deploy-production
needs: [ build, typescript ]
runs-on: ubuntu-latest
concurrency: production_environment
environment:
name: production
url: ${{ env.AZURE_URL_PROD }}
permissions:
id-token: write
steps:
- name: Azure log in
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy container to Azure App Service
uses: azure/webapps-deploy@v3
if: needs.build.outputs.container-tag != ''
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
images: ${{ needs.build.outputs.container-tag }}
- name: Check application health
shell: pwsh
env:
APPLICATION_URL: ${{ env.AZURE_URL_PROD }}
run: |
$delay = 10
$limit = 15
$success = $false
for ($i = 0; $i -lt $limit; $i++) {
$response = $null
try {
$response = Invoke-WebRequest -Uri "${env:APPLICATION_URL}/version" -Method Get -UseBasicParsing
} catch {
$response = $_.Exception.Response
}
if (($null -ne $response) -And ($response.StatusCode -eq 200)) {
$json = $response.Content | ConvertFrom-Json
$version = $json.applicationVersion
if ((-Not [string]::IsNullOrWhiteSpace($version)) -And $version.Contains(${env:GITHUB_SHA})) {
$success = $true
break
}
}
Start-Sleep -Seconds $delay
}
if (-Not $success) {
throw "${env:APPLICATION_URL} did not return a successful status code and the expected version within the time limit after $limit attempts."
}
test:
name: test-production
needs: deploy
runs-on: ubuntu-latest
concurrency: production_environment
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
- name: Setup NuGet cache
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }}
restore-keys: ${{ runner.os }}-nuget-
- name: Setup Playwright cache
uses: actions/cache@v4
with:
key: ${{ runner.os }}-playwright-${{ hashFiles('Directory.Packages.props') }}
path: |
~/AppData/Local/ms-playwright
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
- name: Run end-to-end tests
shell: pwsh
env:
WEBSITE_URL: ${{ env.AZURE_URL_PROD }}
run: dotnet test ./tests/AdventOfCode.Tests --configuration Release --filter Category=EndToEnd --logger "GitHubActions;report-warnings=false" /p:CollectCoverage=false
deploy-aws:
if: github.event.repository.fork == false && github.ref_name == github.event.repository.default_branch
name: deploy-aws
needs: [ build, typescript ]
runs-on: ubuntu-latest
concurrency: aws_environment
environment:
name: aws
url: ${{ env.AWS_URL_PROD }}
permissions:
id-token: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: lambda
- name: Extract CloudFormation Template
shell: pwsh
run: |
$configName = "aws-lambda-tools-defaults.json"
$packageName = "lambda.zip"
$templateName = "serverless.template"
$configPath = Join-Path $pwd $configName
$packagePath = Join-Path $pwd $packageName
$templatePath = Join-Path $pwd $templateName
Add-Type -AssemblyName System.IO.Compression.FileSystem
$package = [System.IO.Compression.ZipFile]::OpenRead($packagePath)
$config = $package.GetEntry($configName)
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($config, $configPath, $true)
$template = $package.GetEntry($templateName)
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($template, $templatePath, $true)
$package.Dispose()
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
- name: Install .NET Lambda tool
shell: pwsh
env:
LAMBDA_TOOLS_VERSION: ${{ needs.build.outputs.lambda-tools-version }}
run: |
dotnet tool install --global Amazon.Lambda.Tools --version ${env:LAMBDA_TOOLS_VERSION}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_DEPLOYMENT_ROLE }}
role-session-name: ${{ github.event.repository.name }}-${{ github.run_id }}-deploy
aws-region: ${{ env.AWS_REGION }}
- name: Deploy CloudFormation Template
shell: pwsh
env:
AWS_CLOUDFORMATION_ROLE: ${{ secrets.AWS_CLOUDFORMATION_ROLE }}
run: |
$description = "Deploy build ${env:GITHUB_RUN_NUMBER} to AWS via GitHub Actions"
$configPath = Join-Path $pwd "aws-lambda-tools-defaults.json"
$packagePath = Join-Path $pwd "lambda.zip"
dotnet-lambda `
deploy-serverless `
${env:AWS_CLOUDFORMATION_STACK} `
--cloudformation-role ${env:AWS_CLOUDFORMATION_ROLE} `
--config-file $configPath `
--disable-interactive true `
--package $packagePath `
--stack-wait true `
--tags "GITHUB_ACTIONS_RUN=${env:GITHUB_RUN_NUMBER}" `
--template-substitutions "Description=$description"
- name: Check application health
shell: pwsh
env:
APPLICATION_URL: ${{ env.AWS_URL_PROD }}
run: |
$delay = 10
$limit = 15
$success = $false
for ($i = 0; $i -lt $limit; $i++) {
$response = $null
try {
$response = Invoke-WebRequest -Uri "${env:APPLICATION_URL}/version" -Method Get -UseBasicParsing
} catch {
$response = $_.Exception.Response
}
if (($null -ne $response) -And ($response.StatusCode -eq 200)) {
$json = $response.Content | ConvertFrom-Json
$version = $json.applicationVersion
if ((-Not [string]::IsNullOrWhiteSpace($version)) -And $version.Contains(${env:GITHUB_SHA})) {
$success = $true
break
}
}
Start-Sleep -Seconds $delay
}
if (-Not $success) {
throw "${env:APPLICATION_URL} did not return a successful status code and the expected version within the time limit after $limit attempts."
}
test-aws:
name: test-aws
needs: deploy-aws
runs-on: ubuntu-latest
concurrency: aws_environment
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
- name: Setup NuGet cache
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }}
restore-keys: ${{ runner.os }}-nuget-
- name: Setup Playwright cache
uses: actions/cache@v4
with:
key: ${{ runner.os }}-playwright-${{ hashFiles('Directory.Packages.props') }}
path: |
~/AppData/Local/ms-playwright
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
- name: Run end-to-end tests
shell: pwsh
env:
WEBSITE_URL: ${{ env.AWS_URL_PROD }}
run: dotnet test ./tests/AdventOfCode.Tests --configuration Release --filter Category=EndToEnd --logger "GitHubActions;report-warnings=false" /p:CollectCoverage=false