From bd810e805b3c1ad1b5e18d5e75040554ace9fe9c Mon Sep 17 00:00:00 2001 From: AJ Date: Thu, 12 Sep 2024 14:10:03 -0500 Subject: [PATCH 1/9] Delete .github/workflows/pages.yml --- .github/workflows/pages.yml | 62 ------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 .github/workflows/pages.yml diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index cedae546..00000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll site to Pages - -on: - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.1' # Not needed with a .ruby-version file - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - - name: Setup Pages - id: pages - uses: actions/configure-pages@v2 - - name: Build with Jekyll - # Outputs to the './_site' directory by default - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" - env: - JEKYLL_ENV: production - - name: Upload artifact - # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v1 - - # Deployment job - deploy: - environment: - name: github-pages - url: "${{ steps.deployment.outputs.page_url }}" - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 From af71b65e4e8e608fdffa62fc5306cc21e23f62c1 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Thu, 12 Sep 2024 14:24:57 -0500 Subject: [PATCH 2/9] Add first workflow for GitHub Actions --- .github/workflows/first-workflow.yml | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/first-workflow.yml diff --git a/.github/workflows/first-workflow.yml b/.github/workflows/first-workflow.yml new file mode 100644 index 00000000..432677b5 --- /dev/null +++ b/.github/workflows/first-workflow.yml @@ -0,0 +1,56 @@ +# The name of the job is what will display on the GitHub repository in the Actions tab. +name: First Workflow + +# The 'on' section tells GitHub under what conditions we want to run this workflow. +# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +# Common scenarios include: + # workflow-dispatch (manual execution) + # issues + # push + # pull_request + # schedule +on: + workflow_dispatch: + issues: + types: [opened] + +# This section covers the work to perform. +# We include one or more jobs in this section. +jobs: + # Each individual job will include details like execution order, + # pre-requisite jobs, and execution platform. + job1: + # We can run jobs on GitHub hosted VM runners in Windows, Ubuntu, and Mac OS. + # We can also run jobs on self-hosted hardware. + runs-on: ubuntu-latest + + # Each job contains one or more steps. A step needs to have at least a name and a command. + steps: + - name: Step one + # The 'run' command executes a shell or command script. Because this is Ubuntu, the + # default run command will be /bin/bash + run: echo "Log from step one" + # This section does not appear in the solution file but demonstrates how to set + # custom variables that will be available in the run script. + env: + VARIABLE_NAME: value + - name: Step two + run: echo "Log from step two" + + job2: + # Job 2 will only run after job 1 completes. + # Removing this 'needs' section would make the jobs run simultaneously. + needs: job1 + runs-on: ubuntu-latest + + steps: + - name: Cowsays + # The 'uses' command executes a remote GitHub action. + # A command like mscoutermarsh/cowsays-action means you can + # find this code at https://github.com/mscoutermarsh/cowsays-action + uses: mscoutermarsh/cowsays-action@master + # The 'with' block includes parameters that the workflow will pass + # to this action. Parameters are all in key-value format. + with: + text: 'Ready for prod--ship it!' + color: 'magenta' From 85aadde4f4b57ad01ce3e1e20b94947bbf02105d Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Thu, 12 Sep 2024 15:05:40 -0500 Subject: [PATCH 3/9] Update message character limit to 250 --- docs/03_improve_deploy_app/0301.md | 2 +- src/Application/src/RazorPagesTestSample/Data/Message.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/03_improve_deploy_app/0301.md b/docs/03_improve_deploy_app/0301.md index 2c32af80..1ee87072 100644 --- a/docs/03_improve_deploy_app/0301.md +++ b/docs/03_improve_deploy_app/0301.md @@ -13,7 +13,7 @@ We have a work machine configured, so now it is time to ensure that everything w ## Description -Developers at Munson's Pickles and Preserves like the Team Messaging System in general, but they consistently bring up issues with one aspect of the application: it limits messages to 200 characters or fewer. Developers have made a case that the app should support messages of up to 250 characters in length instead. +Developers at Munson's Pickles and Preserves like the Team Messaging System in general, but they consistently bring up issues with one aspect of the application: it limits messages to 250 characters or fewer. Developers have made a case that the app should support messages of up to 250 characters in length instead. In this task, you will modify the application to support 250 characters instead of 200. You will also follow a feature branching strategy and use a pull request to bring your change into the `main` branch. If you wish to complete this task using a Test-Driven Design approach, please read the **Advanced Challenges (optional)** section below before making any changes. diff --git a/src/Application/src/RazorPagesTestSample/Data/Message.cs b/src/Application/src/RazorPagesTestSample/Data/Message.cs index ea99cbd6..7e8955d2 100644 --- a/src/Application/src/RazorPagesTestSample/Data/Message.cs +++ b/src/Application/src/RazorPagesTestSample/Data/Message.cs @@ -9,8 +9,8 @@ public class Message [Required] [DataType(DataType.Text)] - [StringLength(200, ErrorMessage = "There's a 200 character limit on messages. Please shorten your message.")] + [StringLength(250, ErrorMessage = "There's a 250 character limit on messages. Please shorten your message.")] public string Text { get; set; } } #endregion -} +} \ No newline at end of file From 54917ec60ace02b327b1b935eaa2236d0ba38b34 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Thu, 12 Sep 2024 16:16:20 -0500 Subject: [PATCH 4/9] Update Newtonsoft.Json package to version 13.0.1 Add unit tests for message text validation --- .../RazorPagesTestSample.Tests.csproj | 15 ++---- .../UnitTests/MessageTests.cs | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs diff --git a/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj b/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj index a66e0a92..51174545 100644 --- a/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj +++ b/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj @@ -1,19 +1,15 @@  - net8.0 - - - - + @@ -22,12 +18,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - - ..\..\src\RazorPagesTestSample\bin\Debug\net8.0\RazorPagesTestSample.dll - + - - + \ No newline at end of file diff --git a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs new file mode 100644 index 00000000..f64c509e --- /dev/null +++ b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; +using RazorPagesTestSample.Data; +using Xunit; + +namespace RazorPagesTestSample.Tests.UnitTests +{ + public class MessageTests + { + [Fact] + public void MessageText_ShouldNotExceed250Characters() + { + // Arrange + var message = new Message + { + Text = new string('a', 251) // 251 characters + }; + var validationContext = new ValidationContext(message); + var validationResults = new List(); + + // Act + var isValid = Validator.TryValidateObject(message, validationContext, validationResults, true); + + // Assert + Assert.False(isValid); + Assert.Contains(validationResults, v => v.ErrorMessage.Contains("250 character limit")); + } + + [Fact] + public void MessageText_ShouldBeValid_WhenWithin250Characters() + { + // Arrange + var message = new Message + { + Text = new string('a', 250) // 250 characters + }; + var validationContext = new ValidationContext(message); + var validationResults = new List(); + + // Act + var isValid = Validator.TryValidateObject(message, validationContext, validationResults, true); + + // Assert + Assert.True(isValid); + } + } +} \ No newline at end of file From 15d1d95ba2a6b127accf235148d4a3a257833494 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Fri, 13 Sep 2024 12:00:40 -0500 Subject: [PATCH 5/9] Add GitHub Actions workflows for Azure deployment and .NET CI, and update error page structure --- .github/workflows/deploy.yml | 35 +++++++ .github/workflows/dotnet-deploy-1.yml | 31 +++++++ .vscode/tasks.json | 22 +++++ .../src/RazorPagesTestSample/Dockerfile | 24 +++++ .../RazorPagesTestSample/Pages/Error.cshtml | 2 +- .../UnitTests/DataAccessLayerTest.cs | 41 ++++++++- .../UnitTests/MessageTests.cs | 39 ++++++++ src/InfrastructureAsCode/main.bicep | 92 ++++++++++++++++++- 8 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/dotnet-deploy-1.yml create mode 100644 .vscode/tasks.json create mode 100644 src/Application/src/RazorPagesTestSample/Dockerfile diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..fb6905d0 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,35 @@ +name: Azure Bicep + +on: + workflow_dispatch + +env: + targetEnv: dev + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + pages: write + id-token: write + steps: + # Checkout code + - uses: actions/checkout@main + + # Log into Azure + - uses: azure/login@v2.1.1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + # Deploy ARM template + - name: Run ARM deploy + uses: azure/arm-deploy@v1 + with: + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resourceGroupName: ${{ secrets.AZURE_RG }} + template: ./src/InfrastructureAsCode/main.bicep + parameters: environment=${{ env.targetEnv }} \ No newline at end of file diff --git a/.github/workflows/dotnet-deploy-1.yml b/.github/workflows/dotnet-deploy-1.yml new file mode 100644 index 00000000..f976499f --- /dev/null +++ b/.github/workflows/dotnet-deploy-1.yml @@ -0,0 +1,31 @@ +name: .NET CI + +on: + push: + branches: [ main ] + paths: + - src/Application/** + pull_request: + branches: [ main ] + paths: + - src/Application/** + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Build + run: dotnet build --no-restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Test + run: dotnet test --no-build --verbosity normal ./src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..cb588375 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,22 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Save All Files", + "command": "${command:workbench.action.files.saveAll}", + "type": "shell", + "problemMatcher": [] + }, + { + "label": "Build Project", + "dependsOn": "Save All Files", + "command": "dotnet build", + "type": "shell", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/Application/src/RazorPagesTestSample/Dockerfile b/src/Application/src/RazorPagesTestSample/Dockerfile new file mode 100644 index 00000000..c4f208e6 --- /dev/null +++ b/src/Application/src/RazorPagesTestSample/Dockerfile @@ -0,0 +1,24 @@ +# Use the official .NET 8 SDK image as a build stage +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /app + +# Copy the project file and restore dependencies +COPY *.csproj . +RUN dotnet restore + +# Copy the rest of the application code +COPY . . + +# Build the application +RUN dotnet publish -c Release -o out + +# Use the official .NET 8 runtime image as a runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app +COPY --from=build /app/out . + +# Set the environment variable for ASP.NET Core HTTP ports +ENV ASPNETCORE_HTTP_PORTS=80 + +# Set the entry point for the application +ENTRYPOINT ["dotnet", "RazorPagesTestSample.dll"] \ No newline at end of file diff --git a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml index 6f92b956..b34f2566 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml +++ b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml @@ -5,7 +5,7 @@ }

Error.

-

An error occurred while processing your request.

+

An error occurred while processing your request.

@if (Model.ShowRequestId) { diff --git a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs index 91a91aaa..0cb535b8 100644 --- a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs +++ b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Xunit; using RazorPagesTestSample.Data; +using System.ComponentModel.DataAnnotations; namespace RazorPagesTestSample.Tests.UnitTests { @@ -25,7 +26,7 @@ public async Task GetMessagesAsync_MessagesAreReturned() // Assert var actualMessages = Assert.IsAssignableFrom>(result); Assert.Equal( - expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), + expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } } @@ -77,7 +78,7 @@ public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound() await db.AddRangeAsync(seedMessages); await db.SaveChangesAsync(); var recId = 1; - var expectedMessages = + var expectedMessages = seedMessages.Where(message => message.Id != recId).ToList(); #endregion @@ -90,7 +91,7 @@ public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound() // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( - expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), + expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); #endregion } @@ -121,10 +122,42 @@ public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound() // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( - expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), + expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } } + + + + //Generate a unit test theory to generate messages of various lengths including 250 and try to validate the message object. + [Theory] + [InlineData(150, true)] + [InlineData(199, true)] + [InlineData(200, true)] + [InlineData(201, true)] + [InlineData(249, true)] + [InlineData(250, true)] + [InlineData(251, false)] + [InlineData(300, false)] + public async Task AddMessageAsync_TestMessageLength(int messageLength, bool expectedValidMessage) + { + using (var db = new AppDbContext(Utilities.TestDbContextOptions())) + { + // Arrange + var recId = 10; + var expectedMessage = new Message() { Id = recId, Text = new string('X', messageLength) }; + + // Act + var isValidMessage = Validator.TryValidateObject(expectedMessage, new ValidationContext(expectedMessage), null, validateAllProperties: true); + + // Simulate an asynchronous operation + await Task.Delay(1); + + // Assert + Assert.Equal(expectedValidMessage, isValidMessage); + } + } + #endregion } } diff --git a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs index f64c509e..5b91f3ae 100644 --- a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs +++ b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/MessageTests.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using RazorPagesTestSample.Data; using Xunit; +using System.Collections.Generic; namespace RazorPagesTestSample.Tests.UnitTests { @@ -42,5 +43,43 @@ public void MessageText_ShouldBeValid_WhenWithin250Characters() // Assert Assert.True(isValid); } + + //test the insertion of a message of length 150 + [Fact] + public void MessageText_ShouldBeValid_WhenWithin150Characters() + { + // Arrange + var message = new Message + { + Text = new string('a', 150) // 150 characters + }; + var validationContext = new ValidationContext(message); + var validationResults = new List(); + + // Act + var isValid = Validator.TryValidateObject(message, validationContext, validationResults, true); + + // Assert + Assert.True(isValid); + } + + //test a message of length 249 + [Fact] + public void MessageText_ShouldBeValid_WhenWithin249Characters() + { + // Arrange + var message = new Message + { + Text = new string('a', 249) // 249 characters + }; + var validationContext = new ValidationContext(message); + var validationResults = new List(); + + // Act + var isValid = Validator.TryValidateObject(message, validationContext, validationResults, true); + + // Assert + Assert.True(isValid); + } } } \ No newline at end of file diff --git a/src/InfrastructureAsCode/main.bicep b/src/InfrastructureAsCode/main.bicep index 6dc69618..d8e740d2 100644 --- a/src/InfrastructureAsCode/main.bicep +++ b/src/InfrastructureAsCode/main.bicep @@ -8,10 +8,98 @@ var webAppName = '${uniqueString(resourceGroup().id)}-${environment}' var appServicePlanName = '${uniqueString(resourceGroup().id)}-mpnp-asp' var logAnalyticsName = '${uniqueString(resourceGroup().id)}-mpnp-la' var appInsightsName = '${uniqueString(resourceGroup().id)}-mpnp-ai' -var sku = 'S1' +var sku = 'P0V3' var registryName = '${uniqueString(resourceGroup().id)}mpnpreg' var registrySku = 'Standard' var imageName = 'techexcel/dotnetcoreapp' var startupCommand = '' -// TODO: complete this script + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: logAnalyticsName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + retentionInDays: 90 + workspaceCapping: { + dailyQuotaGb: 1 + } + } +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02-preview' = { + name: appInsightsName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2020-11-01-preview' = { + name: registryName + location: location + sku: { + name: registrySku + } + properties: { + adminUserEnabled: true + } +} + +resource appServicePlan 'Microsoft.Web/serverFarms@2022-09-01' = { + name: appServicePlanName + location: location + kind: 'linux' + properties: { + reserved: true + } + sku: { + name: sku + } +} + +resource appServiceApp 'Microsoft.Web/sites@2020-12-01' = { + name: webAppName + location: location + properties: { + serverFarmId: appServicePlan.id + httpsOnly: true + clientAffinityEnabled: false + siteConfig: { + linuxFxVersion: 'DOCKER|${containerRegistry.name}.azurecr.io/${uniqueString(resourceGroup().id)}/${imageName}' + http20Enabled: true + minTlsVersion: '1.2' + appCommandLine: startupCommand + appSettings: [ + { + name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE' + value: 'false' + } + { + name: 'DOCKER_REGISTRY_SERVER_URL' + value: 'https://${containerRegistry.name}.azurecr.io' + } + { + name: 'DOCKER_REGISTRY_SERVER_USERNAME' + value: containerRegistry.name + } + { + name: 'DOCKER_REGISTRY_SERVER_PASSWORD' + value: containerRegistry.listCredentials().passwords[0].value + } + { + name: 'APPINSIGHTS_INSTRUMENTATIONKEY' + value: appInsights.properties.InstrumentationKey + } + ] + } + } +} + +output application_name string = appServiceApp.name +output application_url string = appServiceApp.properties.hostNames[0] +output container_registry_name string = containerRegistry.name From 4cc5e82493a1f00ab1806ca48ccd4b76c4047c3e Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Fri, 13 Sep 2024 12:15:14 -0500 Subject: [PATCH 6/9] Update error message heading from h3 to h4 for improved styling --- src/Application/src/RazorPagesTestSample/Pages/Error.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml index b34f2566..3b868b7a 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml +++ b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml @@ -5,7 +5,7 @@ }

Error.

-

An error occurred while processing your request.

+

An error occurred while processing your request.

@if (Model.ShowRequestId) { From 6d7e4643a98fb69eed55ac6295cd3b3f31af02e7 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Fri, 13 Sep 2024 12:43:38 -0500 Subject: [PATCH 7/9] Enhance CI workflow with Docker build and push steps; update error message heading from h4 to h2 for better visibility --- .github/workflows/dotnet-deploy-1.yml | 46 ++++++++++++++++--- .../RazorPagesTestSample/Pages/Error.cshtml | 2 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet-deploy-1.yml b/.github/workflows/dotnet-deploy-1.yml index f976499f..e5214a41 100644 --- a/.github/workflows/dotnet-deploy-1.yml +++ b/.github/workflows/dotnet-deploy-1.yml @@ -1,14 +1,18 @@ name: .NET CI +env: + registryName: mpplabajep.azurecr.io + repositoryName: techexcel/dotnetcoreapp + dockerFolderPath: ./src/Application/src/RazorPagesTestSample + tag: ${{github.run_number}} + on: push: branches: [ main ] - paths: - - src/Application/** + paths: src/Application/** pull_request: branches: [ main ] - paths: - - src/Application/** + paths: src/Application/** # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: @@ -21,11 +25,39 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '8.0.x' - + dotnet-version: 8.0 + - name: Restore dependencies run: dotnet restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj - name: Build run: dotnet build --no-restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj - name: Test - run: dotnet test --no-build --verbosity normal ./src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj \ No newline at end of file + run: dotnet test --no-build --verbosity normal ./src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj + + dockerBuildPush: + + runs-on: ubuntu-latest + needs: build + + steps: + - uses: actions/checkout@v3 + + - name: Docker Login + # You may pin to the exact commit or the version. + # uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + uses: docker/login-action@v1.9.0 + with: + # Server address of Docker registry. If not set then will default to Docker Hub + registry: ${{ secrets.ACR_LOGIN_SERVER }} + # Username used to log against the Docker registry + username: ${{ secrets.ACR_USERNAME }} + # Password or personal access token used to log against the Docker registry + password: ${{ secrets.ACR_PASSWORD }} + # Log out from the Docker registry at the end of a job + logout: true + + - name: Docker Build + run: docker build -t $registryName/$repositoryName:$tag --build-arg build_version=$tag $dockerFolderPath + + - name: Docker Push + run: docker push $registryName/$repositoryName:$tag \ No newline at end of file diff --git a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml index 3b868b7a..6f92b956 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml +++ b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml @@ -5,7 +5,7 @@ }

Error.

-

An error occurred while processing your request.

+

An error occurred while processing your request.

@if (Model.ShowRequestId) { From a73e042857d6777d72862110952bef865fb557d8 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Fri, 13 Sep 2024 12:58:10 -0500 Subject: [PATCH 8/9] Add Azure deployment workflows for dev, test, and prod environments --- .github/workflows/dotnet-deploy-1.yml | 63 ++++++++++++++++++- .../RazorPagesTestSample/Pages/Error.cshtml | 2 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet-deploy-1.yml b/.github/workflows/dotnet-deploy-1.yml index e5214a41..01e27b7c 100644 --- a/.github/workflows/dotnet-deploy-1.yml +++ b/.github/workflows/dotnet-deploy-1.yml @@ -60,4 +60,65 @@ jobs: run: docker build -t $registryName/$repositoryName:$tag --build-arg build_version=$tag $dockerFolderPath - name: Docker Push - run: docker push $registryName/$repositoryName:$tag \ No newline at end of file + run: docker push $registryName/$repositoryName:$tag + + deploy-to-dev: + + runs-on: ubuntu-latest + needs: dockerBuildPush + environment: + name: dev + url: https://mpplabajep-dev.azurewebsites.net/ + + steps: + - name: 'Login via Azure CLI' + uses: azure/login@v2.1.1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'mpplabajep-dev' + images: mpplabajep.azurecr.io/techexcel/dotnetcoreapp:${{github.run_number}} + + deploy-to-test: + + runs-on: ubuntu-latest + needs: deploy-to-dev + environment: + name: test + url: https://mpplabajep-test.azurewebsites.net/ + + steps: + - uses: actions/checkout@v3 + + - name: 'Login via Azure CLI' + uses: azure/login@v2.1.1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'mpplabajep-test' + images: mpplabajep.azurecr.io/techexcel/dotnetcoreapp:${{github.run_number}} + + deploy-to-prod: + + runs-on: ubuntu-latest + needs: deploy-to-test + environment: + name: prod + url: https://mpplabajep-prod.azurewebsites.net/ + + steps: + - uses: actions/checkout@v3 + + - name: 'Login via Azure CLI' + uses: azure/login@v2.1.1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'mpplabajep-prod' + images: mpplabajep.azurecr.io/techexcel/dotnetcoreapp:${{github.run_number}} \ No newline at end of file diff --git a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml index 6f92b956..b34f2566 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml +++ b/src/Application/src/RazorPagesTestSample/Pages/Error.cshtml @@ -5,7 +5,7 @@ }

Error.

-

An error occurred while processing your request.

+

An error occurred while processing your request.

@if (Model.ShowRequestId) { From 7999f45ed923e9128c5683d098a68e76fa40fb75 Mon Sep 17 00:00:00 2001 From: AJ Enns Date: Fri, 13 Sep 2024 13:07:37 -0500 Subject: [PATCH 9/9] Refactor .NET CI workflow to support CI/CD; streamline paths and update .NET version handling --- .github/workflows/dotnet-deploy-1.yml | 81 +++++---------------------- 1 file changed, 14 insertions(+), 67 deletions(-) diff --git a/.github/workflows/dotnet-deploy-1.yml b/.github/workflows/dotnet-deploy-1.yml index 01e27b7c..1831f263 100644 --- a/.github/workflows/dotnet-deploy-1.yml +++ b/.github/workflows/dotnet-deploy-1.yml @@ -1,23 +1,19 @@ -name: .NET CI +name: .NET CI/CD -env: - registryName: mpplabajep.azurecr.io - repositoryName: techexcel/dotnetcoreapp - dockerFolderPath: ./src/Application/src/RazorPagesTestSample - tag: ${{github.run_number}} - on: push: branches: [ main ] - paths: src/Application/** + paths: + - src/Application/** pull_request: branches: [ main ] - paths: src/Application/** + paths: + - src/Application/** # Allows you to run this workflow manually from the Actions tab workflow_dispatch: + jobs: build: - runs-on: ubuntu-latest steps: @@ -25,70 +21,22 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0 - + dotnet-version: '8.0.x' + - name: Restore dependencies - run: dotnet restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + run: dotnet restore src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj - name: Build - run: dotnet build --no-restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + run: dotnet build --no-restore src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj - name: Test - run: dotnet test --no-build --verbosity normal ./src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj - - dockerBuildPush: - - runs-on: ubuntu-latest - needs: build - - steps: - - uses: actions/checkout@v3 - - - name: Docker Login - # You may pin to the exact commit or the version. - # uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c - uses: docker/login-action@v1.9.0 - with: - # Server address of Docker registry. If not set then will default to Docker Hub - registry: ${{ secrets.ACR_LOGIN_SERVER }} - # Username used to log against the Docker registry - username: ${{ secrets.ACR_USERNAME }} - # Password or personal access token used to log against the Docker registry - password: ${{ secrets.ACR_PASSWORD }} - # Log out from the Docker registry at the end of a job - logout: true - - - name: Docker Build - run: docker build -t $registryName/$repositoryName:$tag --build-arg build_version=$tag $dockerFolderPath - - - name: Docker Push - run: docker push $registryName/$repositoryName:$tag - - deploy-to-dev: - - runs-on: ubuntu-latest - needs: dockerBuildPush - environment: - name: dev - url: https://mpplabajep-dev.azurewebsites.net/ - - steps: - - name: 'Login via Azure CLI' - uses: azure/login@v2.1.1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - uses: azure/webapps-deploy@v2 - with: - app-name: 'mpplabajep-dev' - images: mpplabajep.azurecr.io/techexcel/dotnetcoreapp:${{github.run_number}} + run: dotnet test --no-build --verbosity normal src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj deploy-to-test: - runs-on: ubuntu-latest - needs: deploy-to-dev + needs: build environment: name: test url: https://mpplabajep-test.azurewebsites.net/ - + steps: - uses: actions/checkout@v3 @@ -103,13 +51,12 @@ jobs: images: mpplabajep.azurecr.io/techexcel/dotnetcoreapp:${{github.run_number}} deploy-to-prod: - runs-on: ubuntu-latest needs: deploy-to-test environment: name: prod url: https://mpplabajep-prod.azurewebsites.net/ - + steps: - uses: actions/checkout@v3