diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 130b0217..02847f09 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -7,7 +7,7 @@ on: branches: - master env: - VERSION: 10.0.0-rc1 + VERSION: 10.0.0 jobs: build-and-deploy: runs-on: ubuntu-latest @@ -28,6 +28,7 @@ jobs: name: "Release ${{ env.VERSION }}" body: | Version: ${{ env.VERSION }} + Build run: ${{ github.run_number }} Commit: ${{ github.sha }} token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ApiClients.Audit/.docker/docker-compose.yml b/Api.ApiClients.Audit/.docker/docker-compose.yml index 3701e77a..65f425e7 100644 --- a/Api.ApiClients.Audit/.docker/docker-compose.yml +++ b/Api.ApiClients.Audit/.docker/docker-compose.yml @@ -16,7 +16,7 @@ services: hostname: api-apiclients-audit-service restart: on-failure ports: - - 8181:8181 + - 8181:8080 build: context: ../Api.ApiClients.Audit.Service dockerfile: "Dockerfile.Local" diff --git a/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml index 2e9dbcea..3d59297e 100644 --- a/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml +++ b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ApiClients.Audit IMAGE_NAME: api.apiclients.audit SERVICE_NAME: api-apiclients-audit VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ApiClients.Audit/.kubernetes/configmap.yaml b/Api.ApiClients.Audit/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ApiClients.Audit/.kubernetes/configmap.yaml +++ b/Api.ApiClients.Audit/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ApiClients.Audit/.kubernetes/deployment.yaml b/Api.ApiClients.Audit/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ApiClients.Audit/.kubernetes/deployment.yaml +++ b/Api.ApiClients.Audit/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local index 1e6d12c6..4310acdb 100644 --- a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json index dbd68122..d43a066a 100644 --- a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json @@ -5,7 +5,7 @@ "Root": "api", "Http": { "Ports": [ - 8181 + 8080 ] } } diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local b/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local index f55f6733..383c565c 100644 --- a/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Dockerfile b/Api.ApiClients.Audit/Dockerfile index ae5aa0b8..18cc52ae 100644 --- a/Api.ApiClients.Audit/Dockerfile +++ b/Api.ApiClients.Audit/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ApiClients.Entity/.docker/docker-compose.yml b/Api.ApiClients.Entity/.docker/docker-compose.yml index c93a17e7..a51b8975 100644 --- a/Api.ApiClients.Entity/.docker/docker-compose.yml +++ b/Api.ApiClients.Entity/.docker/docker-compose.yml @@ -16,7 +16,7 @@ services: hostname: api-apiclients-entity-service restart: on-failure ports: - - 8181:8181 + - 8181:8080 build: context: ../Api.ApiClients.Entity.Service dockerfile: "Dockerfile.Local" diff --git a/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml index bec796fc..3aeb1fe2 100644 --- a/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml +++ b/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ApiClients.Entity IMAGE_NAME: api.apiclients.entity SERVICE_NAME: api-apiclients-entity VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ApiClients.Entity/.kubernetes/configmap.yaml b/Api.ApiClients.Entity/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ApiClients.Entity/.kubernetes/configmap.yaml +++ b/Api.ApiClients.Entity/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ApiClients.Entity/.kubernetes/deployment.yaml b/Api.ApiClients.Entity/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ApiClients.Entity/.kubernetes/deployment.yaml +++ b/Api.ApiClients.Entity/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local index 9223f500..7ed69ae9 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json index dbd68122..d43a066a 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json @@ -5,7 +5,7 @@ "Root": "api", "Http": { "Ports": [ - 8181 + 8080 ] } } diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local index 72d7216b..3a6a08e0 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json index 62eb606b..ab7ef06a 100644 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json @@ -13,7 +13,7 @@ "NanoApiClient": { "Host": "api.apiclients.entity.service", "Root": "api", - "Port": 8181, + "Port": 8080, "UseSsl": false, "Timeout": "00:00:30", "HealthCheck": { diff --git a/Api.ApiClients.Entity/Dockerfile b/Api.ApiClients.Entity/Dockerfile index 961dbddb..b6bc2925 100644 --- a/Api.ApiClients.Entity/Dockerfile +++ b/Api.ApiClients.Entity/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ApiClients.RootLogIn/.docker/docker-compose.yml b/Api.ApiClients.RootLogIn/.docker/docker-compose.yml index 8f0c55a8..accdcd21 100644 --- a/Api.ApiClients.RootLogIn/.docker/docker-compose.yml +++ b/Api.ApiClients.RootLogIn/.docker/docker-compose.yml @@ -16,7 +16,7 @@ services: hostname: api-apiclients-rootlogin-service restart: on-failure ports: - - 8181:8181 + - 8181:8080 build: context: ../Api.ApiClients.RootLogIn.Service dockerfile: "Dockerfile.Local" diff --git a/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml b/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml index 1fa27e27..e3a91aa2 100644 --- a/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml +++ b/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ApiClients.RootLogIn IMAGE_NAME: api.apiclients.rootlogin SERVICE_NAME: api-apiclients-rootlogin VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml b/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml +++ b/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml b/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml +++ b/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local index e6afa324..18c597d8 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json index f28ebab1..3ecf604f 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json @@ -5,7 +5,7 @@ "Root": "api", "Http": { "Ports": [ - 8181 + 8080 ] } }, diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local index 557a434d..e09e773f 100644 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogIn.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Dockerfile b/Api.ApiClients.RootLogIn/Dockerfile index 2fad135c..f48b44c8 100644 --- a/Api.ApiClients.RootLogIn/Dockerfile +++ b/Api.ApiClients.RootLogIn/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ApiClients/.docker/docker-compose.yml b/Api.ApiClients/.docker/docker-compose.yml index 0129b3b3..ab197d18 100644 --- a/Api.ApiClients/.docker/docker-compose.yml +++ b/Api.ApiClients/.docker/docker-compose.yml @@ -16,7 +16,7 @@ services: hostname: api-apiclients-service restart: on-failure ports: - - 8181:8181 + - 8181:8080 build: context: ../Api.ApiClients.Service dockerfile: "Dockerfile.Local" diff --git a/Api.ApiClients/.github/workflows/build-and-deploy.yml b/Api.ApiClients/.github/workflows/build-and-deploy.yml index f97c3744..9ba21814 100644 --- a/Api.ApiClients/.github/workflows/build-and-deploy.yml +++ b/Api.ApiClients/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ApiClients IMAGE_NAME: api.apiclients SERVICE_NAME: api-apiclients VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ApiClients/.kubernetes/configmap.yaml b/Api.ApiClients/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ApiClients/.kubernetes/configmap.yaml +++ b/Api.ApiClients/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ApiClients/.kubernetes/deployment.yaml b/Api.ApiClients/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ApiClients/.kubernetes/deployment.yaml +++ b/Api.ApiClients/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj b/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj +++ b/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj b/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local b/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local index 5d914dde..17025cf0 100644 --- a/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local +++ b/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.json index 702a5e62..e8d9ff25 100644 --- a/Api.ApiClients/Api.ApiClients.Service/appsettings.json +++ b/Api.ApiClients/Api.ApiClients.Service/appsettings.json @@ -5,7 +5,7 @@ "Root": "api", "Http": { "Ports": [ - 8181 + 8080 ] } } diff --git a/Api.ApiClients/Api.ApiClients/Dockerfile.Local b/Api.ApiClients/Api.ApiClients/Dockerfile.Local index ce847262..6ef7cfd1 100644 --- a/Api.ApiClients/Api.ApiClients/Dockerfile.Local +++ b/Api.ApiClients/Api.ApiClients/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ApiClients.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Dockerfile b/Api.ApiClients/Dockerfile index 554c9c3f..42ba7394 100644 --- a/Api.ApiClients/Dockerfile +++ b/Api.ApiClients/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ApiClients/README.md b/Api.ApiClients/README.md index 6e3af283..06f0aced 100644 --- a/Api.ApiClients/README.md +++ b/Api.ApiClients/README.md @@ -52,7 +52,7 @@ Configured the application with a connection to the `NanoApiClient`. "NanoApiClient": { "Host": "localhost", "Root": "api", - "Port": 8181, + "Port": 8080, "UseSsl": false, "Timeout": "00:00:30", "HealthCheck": { diff --git a/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml index 1b1ae5fa..15b8896d 100644 --- a/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml +++ b/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Auth.External.Custom IMAGE_NAME: api.auth.external.custom SERVICE_NAME: api-auth-external-custom VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,22 +34,21 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -165,7 +159,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -173,6 +167,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Auth.External.Custom/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Auth.External.Custom/.kubernetes/configmap.yaml +++ b/Api.Auth.External.Custom/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Auth.External.Custom/.kubernetes/deployment.yaml index aca5889f..6ea6a615 100644 --- a/Api.Auth.External.Custom/.kubernetes/deployment.yaml +++ b/Api.Auth.External.Custom/.kubernetes/deployment.yaml @@ -84,6 +84,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj b/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local b/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local index 4d00259a..d95cf1e7 100644 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Auth.External.Custom/Dockerfile b/Api.Auth.External.Custom/Dockerfile index 2a07e09b..63fc7b88 100644 --- a/Api.Auth.External.Custom/Dockerfile +++ b/Api.Auth.External.Custom/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Auth.External.Custom/README.md b/Api.Auth.External.Custom/README.md index dc8fc4a4..5252ffaf 100644 --- a/Api.Auth.External.Custom/README.md +++ b/Api.Auth.External.Custom/README.md @@ -82,6 +82,30 @@ Configured the application with the necessary authentication setup. } ``` +...and for `Staging` and `Production` environments. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } +} +``` + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } +} +``` + ## Kubernetes For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be enabled. Below demonstrates how to map the secret containing the JWT keys. diff --git a/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml b/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml index b33ac0de..8283c158 100644 --- a/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml +++ b/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Auth.RootLogin IMAGE_NAME: api.auth.rootlogin SERVICE_NAME: api-auth-rootlogin VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,22 +34,21 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -165,7 +159,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -173,6 +167,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Auth.RootLogin/.kubernetes/configmap.yaml b/Api.Auth.RootLogin/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Auth.RootLogin/.kubernetes/configmap.yaml +++ b/Api.Auth.RootLogin/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Auth.RootLogin/.kubernetes/deployment.yaml b/Api.Auth.RootLogin/.kubernetes/deployment.yaml index aca5889f..6ea6a615 100644 --- a/Api.Auth.RootLogin/.kubernetes/deployment.yaml +++ b/Api.Auth.RootLogin/.kubernetes/deployment.yaml @@ -84,6 +84,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj b/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local b/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local index d87906a5..97c76dbc 100644 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.dll"] \ No newline at end of file diff --git a/Api.Auth.RootLogin/Dockerfile b/Api.Auth.RootLogin/Dockerfile index 3330579b..30705e8f 100644 --- a/Api.Auth.RootLogin/Dockerfile +++ b/Api.Auth.RootLogin/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Auth.RootLogin/README.md b/Api.Auth.RootLogin/README.md index a7c64602..b11731fb 100644 --- a/Api.Auth.RootLogin/README.md +++ b/Api.Auth.RootLogin/README.md @@ -82,6 +82,30 @@ Configured the application with the necessary authentication setup. } ``` +...and for `Staging` and `Production` environments. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } +} +``` + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } +} +``` + ## Kubernetes For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be enabled. Below demonstrates how to map the secret containing the JWT keys. @@ -114,11 +138,3 @@ env: ``` ...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Authorization/.github/workflows/build-and-deploy.yml b/Api.Authorization/.github/workflows/build-and-deploy.yml index 27a37e6b..3be9faad 100644 --- a/Api.Authorization/.github/workflows/build-and-deploy.yml +++ b/Api.Authorization/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Authorization IMAGE_NAME: api.authorization SERVICE_NAME: api-authorization VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,22 +34,21 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -165,7 +159,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -173,6 +167,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Authorization/.kubernetes/configmap.yaml b/Api.Authorization/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Authorization/.kubernetes/configmap.yaml +++ b/Api.Authorization/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Authorization/.kubernetes/deployment.yaml b/Api.Authorization/.kubernetes/deployment.yaml index aca5889f..6ea6a615 100644 --- a/Api.Authorization/.kubernetes/deployment.yaml +++ b/Api.Authorization/.kubernetes/deployment.yaml @@ -84,6 +84,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj b/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj +++ b/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Authorization/Api.Authorization/Dockerfile.Local b/Api.Authorization/Api.Authorization/Dockerfile.Local index bd2f4fec..a4fd5442 100644 --- a/Api.Authorization/Api.Authorization/Dockerfile.Local +++ b/Api.Authorization/Api.Authorization/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Authorization.dll"] \ No newline at end of file diff --git a/Api.Authorization/Dockerfile b/Api.Authorization/Dockerfile index 3a70d191..19ff6910 100644 --- a/Api.Authorization/Dockerfile +++ b/Api.Authorization/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml b/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml index 2b200b6d..d64e353f 100644 --- a/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml +++ b/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ContentNegotiation IMAGE_NAME: api.contentnegotiation SERVICE_NAME: api-contentnegotiation VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ContentNegotiation/.kubernetes/configmap.yaml b/Api.ContentNegotiation/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ContentNegotiation/.kubernetes/configmap.yaml +++ b/Api.ContentNegotiation/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ContentNegotiation/.kubernetes/deployment.yaml b/Api.ContentNegotiation/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ContentNegotiation/.kubernetes/deployment.yaml +++ b/Api.ContentNegotiation/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj b/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj +++ b/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local b/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local index dc9daf88..f9204f6d 100644 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ContentNegotiation.dll"] \ No newline at end of file diff --git a/Api.ContentNegotiation/Dockerfile b/Api.ContentNegotiation/Dockerfile index 658250a4..d2576397 100644 --- a/Api.ContentNegotiation/Dockerfile +++ b/Api.ContentNegotiation/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Cookies/.github/workflows/build-and-deploy.yml b/Api.Cookies/.github/workflows/build-and-deploy.yml index 3a341c47..0e78026c 100644 --- a/Api.Cookies/.github/workflows/build-and-deploy.yml +++ b/Api.Cookies/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Cookies IMAGE_NAME: api.cookies SERVICE_NAME: api-cookies VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Cookies/.kubernetes/configmap.yaml b/Api.Cookies/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Cookies/.kubernetes/configmap.yaml +++ b/Api.Cookies/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Cookies/.kubernetes/deployment.yaml b/Api.Cookies/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Cookies/.kubernetes/deployment.yaml +++ b/Api.Cookies/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj b/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj +++ b/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Cookies/Api.Cookies/Dockerfile.Local b/Api.Cookies/Api.Cookies/Dockerfile.Local index 09cc9dd0..04fd490c 100644 --- a/Api.Cookies/Api.Cookies/Dockerfile.Local +++ b/Api.Cookies/Api.Cookies/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Cookies.dll"] \ No newline at end of file diff --git a/Api.Cookies/Dockerfile b/Api.Cookies/Dockerfile index 2f4f37d9..7985f6cc 100644 --- a/Api.Cookies/Dockerfile +++ b/Api.Cookies/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml index add53801..56f5faa3 100644 --- a/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml +++ b/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.CustomConfigSection IMAGE_NAME: api.customconfigsection SERVICE_NAME: api-customconfigsection VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.CustomConfigSection/.kubernetes/configmap.yaml b/Api.CustomConfigSection/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.CustomConfigSection/.kubernetes/configmap.yaml +++ b/Api.CustomConfigSection/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.CustomConfigSection/.kubernetes/deployment.yaml b/Api.CustomConfigSection/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.CustomConfigSection/.kubernetes/deployment.yaml +++ b/Api.CustomConfigSection/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj b/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj +++ b/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local b/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local index 96d8dc2e..5e73c684 100644 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Api.CustomConfigSection/Dockerfile b/Api.CustomConfigSection/Dockerfile index ee947826..8bf39f61 100644 --- a/Api.CustomConfigSection/Dockerfile +++ b/Api.CustomConfigSection/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml b/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml index b1ae72ca..ee9281bc 100644 --- a/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml +++ b/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.CustomMiddleware IMAGE_NAME: api.custommiddleware SERVICE_NAME: api-custommiddleware VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.CustomMiddleware/.kubernetes/configmap.yaml b/Api.CustomMiddleware/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.CustomMiddleware/.kubernetes/configmap.yaml +++ b/Api.CustomMiddleware/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.CustomMiddleware/.kubernetes/deployment.yaml b/Api.CustomMiddleware/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.CustomMiddleware/.kubernetes/deployment.yaml +++ b/Api.CustomMiddleware/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj b/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj +++ b/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local b/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local index e059926f..40ba371d 100644 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.CustomMiddleware.dll"] \ No newline at end of file diff --git a/Api.CustomMiddleware/Dockerfile b/Api.CustomMiddleware/Dockerfile index a5772da5..102e9115 100644 --- a/Api.CustomMiddleware/Dockerfile +++ b/Api.CustomMiddleware/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.CustomService/.github/workflows/build-and-deploy.yml b/Api.CustomService/.github/workflows/build-and-deploy.yml index 4cb38d5a..8f88f093 100644 --- a/Api.CustomService/.github/workflows/build-and-deploy.yml +++ b/Api.CustomService/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.CustomService IMAGE_NAME: api.customservice SERVICE_NAME: api-customservice VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.CustomService/.kubernetes/configmap.yaml b/Api.CustomService/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.CustomService/.kubernetes/configmap.yaml +++ b/Api.CustomService/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.CustomService/.kubernetes/deployment.yaml b/Api.CustomService/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.CustomService/.kubernetes/deployment.yaml +++ b/Api.CustomService/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj b/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj +++ b/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.CustomService/Api.CustomService/Dockerfile.Local b/Api.CustomService/Api.CustomService/Dockerfile.Local index 83ac0c36..21f64860 100644 --- a/Api.CustomService/Api.CustomService/Dockerfile.Local +++ b/Api.CustomService/Api.CustomService/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.CustomService.dll"] \ No newline at end of file diff --git a/Api.CustomService/Dockerfile b/Api.CustomService/Dockerfile index 8679c6c7..4265f374 100644 --- a/Api.CustomService/Dockerfile +++ b/Api.CustomService/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Audit/.docker/docker-compose.yml b/Api.Data.Audit/.docker/docker-compose.yml index ee96146f..0b053f4a 100644 --- a/Api.Data.Audit/.docker/docker-compose.yml +++ b/Api.Data.Audit/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Audit/.github/workflows/build-and-deploy.yml b/Api.Data.Audit/.github/workflows/build-and-deploy.yml index e8e13f4a..d5c6262c 100644 --- a/Api.Data.Audit/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Audit/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Audit IMAGE_NAME: api.data.audit SERVICE_NAME: api-data-audit VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml b/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Audit/.kubernetes/configmap.yaml b/Api.Data.Audit/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Audit/.kubernetes/configmap.yaml +++ b/Api.Data.Audit/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Audit/.kubernetes/deployment.yaml b/Api.Data.Audit/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.Audit/.kubernetes/deployment.yaml +++ b/Api.Data.Audit/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj b/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj +++ b/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local b/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local index 02405ae6..4660beaf 100644 --- a/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local +++ b/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Audit.dll"] \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json +++ b/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Audit/Dockerfile b/Api.Data.Audit/Dockerfile index d8e60daa..a58c06ab 100644 --- a/Api.Data.Audit/Dockerfile +++ b/Api.Data.Audit/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.EntityEvents/.docker/docker-compose.yml b/Api.Data.EntityEvents/.docker/docker-compose.yml index c7513f0a..de7e7113 100644 --- a/Api.Data.EntityEvents/.docker/docker-compose.yml +++ b/Api.Data.EntityEvents/.docker/docker-compose.yml @@ -36,8 +36,8 @@ services: networks: - network environment: - MYSQL_ROOT_PASSWORD: myPassword_123 MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql diff --git a/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml b/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml index ab07e819..7ebe9759 100644 --- a/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml +++ b/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.EntityEvents IMAGE_NAME: api.data.entityevents SERVICE_NAME: api-data-entityevents VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml b/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.EntityEvents/.kubernetes/configmap.yaml b/Api.Data.EntityEvents/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.EntityEvents/.kubernetes/configmap.yaml +++ b/Api.Data.EntityEvents/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.EntityEvents/.kubernetes/deployment.yaml b/Api.Data.EntityEvents/.kubernetes/deployment.yaml index ddbdae40..5181e537 100644 --- a/Api.Data.EntityEvents/.kubernetes/deployment.yaml +++ b/Api.Data.EntityEvents/.kubernetes/deployment.yaml @@ -48,7 +48,6 @@ spec: secretKeyRef: name: rabbitmq-auth key: username - envFrom: - name: Eventing__Credentials__Secret valueFrom: secretKeyRef: @@ -90,6 +89,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj index c58bf942..1a71ff2f 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj index c58bf942..1a71ff2f 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local index 6d48bd26..7b2731b0 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.Subscriber.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json index 4de4ef5b..4cd2baad 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb2;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb2;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local index b78f25ae..019c7fef 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json index 74c8964f..3ccddc18 100644 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json @@ -1,7 +1,7 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" }, "Eventing": { "Credentials": { diff --git a/Api.Data.EntityEvents/Dockerfile b/Api.Data.EntityEvents/Dockerfile index dca7eca5..5d8feeb5 100644 --- a/Api.Data.EntityEvents/Dockerfile +++ b/Api.Data.EntityEvents/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml b/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml index d7305c76..e8a95380 100644 --- a/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml +++ b/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml index e43c9804..66e9d4cd 100644 --- a/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Identity.Auth.ApiKey IMAGE_NAME: api.data.identity.auth.apikey SERVICE_NAME: api-data-identity.auth.apikey VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,25 +38,24 @@ env: KUBERNETES_CPU_SCALING: 180 SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -69,86 +68,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -177,7 +178,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -229,7 +230,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -237,6 +238,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml index c8c3659c..3824a48e 100644 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml @@ -94,6 +94,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local index bab003df..4b93dba9 100644 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.ApiKey.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json index 223c8a7b..12cbb219 100644 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json @@ -13,6 +13,6 @@ }, "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Dockerfile b/Api.Data.Identity.Auth.ApiKey/Dockerfile index 30ab00dc..94f9f717 100644 --- a/Api.Data.Identity.Auth.ApiKey/Dockerfile +++ b/Api.Data.Identity.Auth.ApiKey/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Identity.Auth.ApiKey/README.md b/Api.Data.Identity.Auth.ApiKey/README.md index 45050da2..5f34e140 100644 --- a/Api.Data.Identity.Auth.ApiKey/README.md +++ b/Api.Data.Identity.Auth.ApiKey/README.md @@ -48,17 +48,17 @@ Additionally, the identity controller is also avaialble, and the actions can be Configured the application with the necessary authentication setup, in addition to the existing identity and jwt configuration. ```json -"App": { - "Data": { - "Identity": { - "ApiKey": { - "Secret": "secret" - } +"Data": { + "Identity": { + "ApiKey": { + "Secret": "secret" } } } ``` +Additionally, JWT authentication has been configured to enable the application to generate JWT tokens based on an API key. + ## Kubernetes For `Staging` and `Production` environments, a secret must be created to securely store the apikey secret. Below demonstrates how to map the secret containing the API key secret. diff --git a/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml b/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml index 921f1be5..77810132 100644 --- a/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml +++ b/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml index 49b0d6db..31fc303f 100644 --- a/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Identity.Auth.External.Custom IMAGE_NAME: api.data.identity.auth.external.custom SERVICE_NAME: api-data-identity.auth.external.custom VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,24 +38,23 @@ env: KUBERNETES_CPU_SCALING: 180 SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -68,86 +67,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -176,7 +177,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -221,7 +222,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -229,6 +230,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml index f615e1a7..7b7d2e61 100644 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml @@ -89,6 +89,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local index a00c6450..8a572c27 100644 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json index 223c8a7b..12cbb219 100644 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json @@ -13,6 +13,6 @@ }, "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Dockerfile b/Api.Data.Identity.Auth.External.Custom/Dockerfile index 5a2e6a5e..38d1a8bd 100644 --- a/Api.Data.Identity.Auth.External.Custom/Dockerfile +++ b/Api.Data.Identity.Auth.External.Custom/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Identity.Auth.External.Custom/README.md b/Api.Data.Identity.Auth.External.Custom/README.md index f9e7730c..43ebf51f 100644 --- a/Api.Data.Identity.Auth.External.Custom/README.md +++ b/Api.Data.Identity.Auth.External.Custom/README.md @@ -14,7 +14,6 @@ Nano is referenced directly from source (not via NuGet packages) and is expected ## Table of Contents * [Summary](#summary) -* [Configuration](#configuration) ## Summary This application builds on **[Api.Data.Identity.Auth.Jwt](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity.Auth.Jwt)**. @@ -51,36 +50,3 @@ The following new endpoints related to the custom authentication provider from t > 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. -## Configuration -Configured the application with the necessary authentication setup, in addition to the identity configuration. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } -} -``` - -...and for `appesettings.Developmnet.json`. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", - "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", - "Expiration": "24:00:00" - } - } -} -``` diff --git a/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml b/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml index 69136e45..599c4f0c 100644 --- a/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml +++ b/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml index 7141f0f2..0975693f 100644 --- a/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Identity.Auth.Jwt IMAGE_NAME: api.data.identity.auth.jwt SERVICE_NAME: api-data-identity.auth.jwt VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,24 +38,23 @@ env: KUBERNETES_CPU_SCALING: 180 SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -68,86 +67,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -176,7 +177,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -221,7 +222,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -229,6 +230,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml index f615e1a7..7b7d2e61 100644 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml @@ -89,6 +89,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local index 678ca81b..ee5ad4a0 100644 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.Jwt.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json index 223c8a7b..12cbb219 100644 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json @@ -13,6 +13,6 @@ }, "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Dockerfile b/Api.Data.Identity.Auth.Jwt/Dockerfile index d166ebde..9c666c21 100644 --- a/Api.Data.Identity.Auth.Jwt/Dockerfile +++ b/Api.Data.Identity.Auth.Jwt/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Identity.Auth.Jwt/README.md b/Api.Data.Identity.Auth.Jwt/README.md index bfbaf354..a8c3c174 100644 --- a/Api.Data.Identity.Auth.Jwt/README.md +++ b/Api.Data.Identity.Auth.Jwt/README.md @@ -75,8 +75,32 @@ Configured the application with the necessary authentication setup, in addition } ``` +...and for `Staging` and `Production` environments. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } +} +``` + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } +} +``` + ## Kubernetes -For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be +For `Staging` and `Production` environments, a `auth-jwt-secret.yaml` is also created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be enabled. Below demonstrates how to map the secret containing the JWT keys. ```yaml @@ -98,7 +122,7 @@ spec: ``` ## GitHub Action -The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. +The secrets defined in GitHub must also be inbcluded in environmental variables for `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. ```yaml env: @@ -107,12 +131,3 @@ env: ``` ...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` - diff --git a/Api.Data.Identity/.docker/docker-compose.yml b/Api.Data.Identity/.docker/docker-compose.yml index d4a7e47a..6b6b16db 100644 --- a/Api.Data.Identity/.docker/docker-compose.yml +++ b/Api.Data.Identity/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Identity/.github/workflows/build-and-deploy.yml b/Api.Data.Identity/.github/workflows/build-and-deploy.yml index ec82c6b7..2ab2015a 100644 --- a/Api.Data.Identity/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Identity/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Identity IMAGE_NAME: api.data.identity SERVICE_NAME: api-data-identity VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Identity/.kubernetes/configmap.yaml b/Api.Data.Identity/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Identity/.kubernetes/configmap.yaml +++ b/Api.Data.Identity/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Identity/.kubernetes/deployment.yaml b/Api.Data.Identity/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.Identity/.kubernetes/deployment.yaml +++ b/Api.Data.Identity/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj b/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj +++ b/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local b/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local index a05f0920..44a50c05 100644 --- a/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local +++ b/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Identity.dll"] \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Identity/Dockerfile b/Api.Data.Identity/Dockerfile index 0e6712aa..bdbd7948 100644 --- a/Api.Data.Identity/Dockerfile +++ b/Api.Data.Identity/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.InMemory/.github/workflows/build-and-deploy.yml b/Api.Data.InMemory/.github/workflows/build-and-deploy.yml index 50a2894a..4b960581 100644 --- a/Api.Data.InMemory/.github/workflows/build-and-deploy.yml +++ b/Api.Data.InMemory/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.InMemory IMAGE_NAME: api.data.inmemory SERVICE_NAME: api-data-inmemory VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.InMemory/.kubernetes/configmap.yaml b/Api.Data.InMemory/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.InMemory/.kubernetes/configmap.yaml +++ b/Api.Data.InMemory/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.InMemory/.kubernetes/deployment.yaml b/Api.Data.InMemory/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Data.InMemory/.kubernetes/deployment.yaml +++ b/Api.Data.InMemory/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj b/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj index f949c7de..c706da29 100644 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local b/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local index a61be1c9..e4500893 100644 --- a/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local +++ b/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.InMemory.dll"] \ No newline at end of file diff --git a/Api.Data.InMemory/Dockerfile b/Api.Data.InMemory/Dockerfile index ff662879..d56f83d7 100644 --- a/Api.Data.InMemory/Dockerfile +++ b/Api.Data.InMemory/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.InMemory/README.md b/Api.Data.InMemory/README.md index bb283b02..2bdd8a89 100644 --- a/Api.Data.InMemory/README.md +++ b/Api.Data.InMemory/README.md @@ -68,7 +68,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": "nanoDb", "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, diff --git a/Api.Data.LazyLoading/.docker/docker-compose.yml b/Api.Data.LazyLoading/.docker/docker-compose.yml index b31fee18..986f0034 100644 --- a/Api.Data.LazyLoading/.docker/docker-compose.yml +++ b/Api.Data.LazyLoading/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml b/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml index 2d8de195..93b4c88f 100644 --- a/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml +++ b/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.LazyLoading IMAGE_NAME: api.data.lazyloading SERVICE_NAME: api-data-lazyloading VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml b/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.LazyLoading/.kubernetes/configmap.yaml b/Api.Data.LazyLoading/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.LazyLoading/.kubernetes/configmap.yaml +++ b/Api.Data.LazyLoading/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.LazyLoading/.kubernetes/deployment.yaml b/Api.Data.LazyLoading/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.LazyLoading/.kubernetes/deployment.yaml +++ b/Api.Data.LazyLoading/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local b/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local index ad93161d..4aeaeeb0 100644 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.LazyLoading.dll"] \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.LazyLoading/Dockerfile b/Api.Data.LazyLoading/Dockerfile index 14732284..42bbc82f 100644 --- a/Api.Data.LazyLoading/Dockerfile +++ b/Api.Data.LazyLoading/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql.Collation/.docker/docker-compose.yml b/Api.Data.MySql.Collation/.docker/docker-compose.yml index edbf3b37..6b453c40 100644 --- a/Api.Data.MySql.Collation/.docker/docker-compose.yml +++ b/Api.Data.MySql.Collation/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml index 530f1d65..32d175e6 100644 --- a/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql.Collation IMAGE_NAME: api.data.mysql.collation SERVICE_NAME: api-data-mysql-collation VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,20 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +62,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +172,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +210,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +218,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql.Collation/.kubernetes/configmap.yaml b/Api.Data.MySql.Collation/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql.Collation/.kubernetes/configmap.yaml +++ b/Api.Data.MySql.Collation/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql.Collation/.kubernetes/deployment.yaml b/Api.Data.MySql.Collation/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.MySql.Collation/.kubernetes/deployment.yaml +++ b/Api.Data.MySql.Collation/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local index f2716c69..bb8d99b2 100644 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.Collation.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Dockerfile b/Api.Data.MySql.Collation/Dockerfile index 0d24bca4..7a43e461 100644 --- a/Api.Data.MySql.Collation/Dockerfile +++ b/Api.Data.MySql.Collation/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql.Mappings/.docker/docker-compose.yml b/Api.Data.MySql.Mappings/.docker/docker-compose.yml index 7fe5c2e8..dfecb5b3 100644 --- a/Api.Data.MySql.Mappings/.docker/docker-compose.yml +++ b/Api.Data.MySql.Mappings/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml index 4e0460d8..eb341f86 100644 --- a/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql.Mappings IMAGE_NAME: api.data.mysql.mappings SERVICE_NAME: api-data-mysql-mappings VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml b/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml +++ b/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml b/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml +++ b/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local index 88ec81d2..7532a55f 100644 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.Mappings.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Dockerfile b/Api.Data.MySql.Mappings/Dockerfile index bd6047d9..c10505dc 100644 --- a/Api.Data.MySql.Mappings/Dockerfile +++ b/Api.Data.MySql.Mappings/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql.Spatial/.docker/docker-compose.yml b/Api.Data.MySql.Spatial/.docker/docker-compose.yml index 309848ae..7c9e8d48 100644 --- a/Api.Data.MySql.Spatial/.docker/docker-compose.yml +++ b/Api.Data.MySql.Spatial/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml index 956aec17..5dc3a3d4 100644 --- a/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql.Spatial IMAGE_NAME: api.data.mysql.spatial SERVICE_NAME: api-data-mysql-spatial VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml b/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml +++ b/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml b/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml index 7e8688ef..d910429c 100644 --- a/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml +++ b/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml @@ -79,6 +79,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local index 33203aa6..414bb67a 100644 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Dockerfile b/Api.Data.MySql.Spatial/Dockerfile index dce34833..2579789c 100644 --- a/Api.Data.MySql.Spatial/Dockerfile +++ b/Api.Data.MySql.Spatial/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml b/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml index 5f01dfb6..58e90a83 100644 --- a/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml +++ b/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml index d7056b63..32bb90c6 100644 --- a/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql.StoredProcedures IMAGE_NAME: api.data.mysql.storedprocedures SERVICE_NAME: api-data-mysql-storedprocedures VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local index e3651ec7..b1585bdc 100644 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.StoredProcedures.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Dockerfile b/Api.Data.MySql.StoredProcedures/Dockerfile index 81df7ff6..feba0098 100644 --- a/Api.Data.MySql.StoredProcedures/Dockerfile +++ b/Api.Data.MySql.StoredProcedures/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql.Views/.docker/docker-compose.yml b/Api.Data.MySql.Views/.docker/docker-compose.yml index 8a5c7578..4f52ca85 100644 --- a/Api.Data.MySql.Views/.docker/docker-compose.yml +++ b/Api.Data.MySql.Views/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml index 2466d7bf..7668a2b9 100644 --- a/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql.Views IMAGE_NAME: api.data.mysql.views SERVICE_NAME: api-data-mysql-views VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql.Views/.kubernetes/configmap.yaml b/Api.Data.MySql.Views/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql.Views/.kubernetes/configmap.yaml +++ b/Api.Data.MySql.Views/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql.Views/.kubernetes/deployment.yaml b/Api.Data.MySql.Views/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.MySql.Views/.kubernetes/deployment.yaml +++ b/Api.Data.MySql.Views/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local b/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local index bac8ec3f..a71eddd5 100644 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.Views.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql.Views/Dockerfile b/Api.Data.MySql.Views/Dockerfile index 9e00750f..7922740b 100644 --- a/Api.Data.MySql.Views/Dockerfile +++ b/Api.Data.MySql.Views/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql/.docker/docker-compose.yml b/Api.Data.MySql/.docker/docker-compose.yml index e1757ea8..05a59213 100644 --- a/Api.Data.MySql/.docker/docker-compose.yml +++ b/Api.Data.MySql/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.MySql/.github/workflows/build-and-deploy.yml b/Api.Data.MySql/.github/workflows/build-and-deploy.yml index cba9bb65..9cd43178 100644 --- a/Api.Data.MySql/.github/workflows/build-and-deploy.yml +++ b/Api.Data.MySql/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.MySql IMAGE_NAME: api.data.mysql SERVICE_NAME: api-data-mysql VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.MySql/.kubernetes/configmap.yaml b/Api.Data.MySql/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.MySql/.kubernetes/configmap.yaml +++ b/Api.Data.MySql/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.MySql/.kubernetes/deployment.yaml b/Api.Data.MySql/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.MySql/.kubernetes/deployment.yaml +++ b/Api.Data.MySql/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj b/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj index 1a34a185..0c881493 100644 --- a/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj +++ b/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local b/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local index 2c0d1470..daf3f124 100644 --- a/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local +++ b/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.MySql.dll"] \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json +++ b/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.MySql/Dockerfile b/Api.Data.MySql/Dockerfile index d88ccf17..680e6c3b 100644 --- a/Api.Data.MySql/Dockerfile +++ b/Api.Data.MySql/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.MySql/README.md b/Api.Data.MySql/README.md index 73a6b67a..6d1dd5ec 100644 --- a/Api.Data.MySql/README.md +++ b/Api.Data.MySql/README.md @@ -81,7 +81,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": null, "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, @@ -125,7 +125,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `deployment.yaml`. ```json spec: @@ -145,6 +145,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -157,33 +159,56 @@ Additionally, this step has been added to ensure database migrations are applied - name: Database Migration & User shell: pwsh run: | - $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName; - $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort; - $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - apt-get update; - apt-get install -y mysql-client; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD - $userExists = mysql --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');" $env:SQL_MIGRATION_CONNECTIONSTRING; + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; if ($userExists -eq 0) { - mysql --connect-expired-password -e " ` - CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:SQL_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:SQL_MIGRATION_CONNECTIONSTRING; + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; +``` + +Last, before applying the new Kubernetes templates, these environmental variables must be set. + +```powershell +$env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; ``` -Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. +Finally, apply the templates. diff --git a/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml index 070e0c51..8337fcc2 100644 --- a/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml +++ b/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.PostgreSQL.Spatial IMAGE_NAME: api.data.postgresql.spatial SERVICE_NAME: api-data-postgresql-spatial VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-postgres-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,68 +65,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | @@ -136,10 +132,14 @@ jobs: $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = $env:SQL_MIGRATION_CONNECTIONSTRING; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -220,7 +220,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -228,6 +228,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj index 9b2d874d..9fb055c4 100644 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local index 9e8b385b..67d117e3 100644 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Dockerfile b/Api.Data.PostgreSQL.Spatial/Dockerfile index a1c9d769..5231bcb9 100644 --- a/Api.Data.PostgreSQL.Spatial/Dockerfile +++ b/Api.Data.PostgreSQL.Spatial/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml index 2e82e0f1..1c3f79d0 100644 --- a/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml +++ b/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.PostgreSQL IMAGE_NAME: api.data.postgresql SERVICE_NAME: api-data-postgresql VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-postgres-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,80 +65,81 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "5432"; $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; + + $env:DATA__CONNECTIONSTRING = $env:SQL_MIGRATION_CONNECTIONSTRING; - dotnet ef database update ` + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -182,7 +182,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -220,7 +220,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -228,6 +228,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml b/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.PostgreSQL/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.PostgreSQL/.kubernetes/configmap.yaml +++ b/Api.Data.PostgreSQL/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.PostgreSQL/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL/.kubernetes/deployment.yaml index 7e8688ef..d910429c 100644 --- a/Api.Data.PostgreSQL/.kubernetes/deployment.yaml +++ b/Api.Data.PostgreSQL/.kubernetes/deployment.yaml @@ -79,6 +79,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj index 9b2d874d..9fb055c4 100644 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local index e3fe4a49..2ed1cd4f 100644 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Dockerfile b/Api.Data.PostgreSQL/Dockerfile index 754207ea..5afe6e4b 100644 --- a/Api.Data.PostgreSQL/Dockerfile +++ b/Api.Data.PostgreSQL/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.PostgreSQL/README.md b/Api.Data.PostgreSQL/README.md index 982e730e..f2fa31cd 100644 --- a/Api.Data.PostgreSQL/README.md +++ b/Api.Data.PostgreSQL/README.md @@ -81,7 +81,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": null, "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, @@ -123,7 +123,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `deployment.yaml`. ```json spec: @@ -143,6 +143,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-postgres-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -160,18 +162,19 @@ Additionally, this step has been added to ensure database migrations are applied $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + $env:DATA__CONNECTIONSTRING = $env:SQL_MIGRATION_CONNECTIONSTRING; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - - apt-get update - apt-get install -y postgresql-client $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" @@ -199,6 +202,15 @@ Additionally, this step has been added to ensure database migrations are applied psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; +``` + +Last, before applying the new Kubernetes templates, these environmental variables must be set. + +```powershell +$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; ``` -Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. +Finally, apply the templates. diff --git a/Api.Data.Repository.AutoSave/.docker/docker-compose.yml b/Api.Data.Repository.AutoSave/.docker/docker-compose.yml index 9b69f42b..3b06ee7b 100644 --- a/Api.Data.Repository.AutoSave/.docker/docker-compose.yml +++ b/Api.Data.Repository.AutoSave/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml index cac9c331..531a6e7f 100644 --- a/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Repository.AutoSave IMAGE_NAME: api.data.repository.autosave SERVICE_NAME: api-data-repository-autosave VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml b/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml b/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml +++ b/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml b/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml +++ b/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local index 265ee7c3..c7eec079 100644 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Repository.AutoSave.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Dockerfile b/Api.Data.Repository.AutoSave/Dockerfile index 3c02c42d..04f4949f 100644 --- a/Api.Data.Repository.AutoSave/Dockerfile +++ b/Api.Data.Repository.AutoSave/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.Repository.Includes/.docker/docker-compose.yml b/Api.Data.Repository.Includes/.docker/docker-compose.yml index 98336e73..bb0a018c 100644 --- a/Api.Data.Repository.Includes/.docker/docker-compose.yml +++ b/Api.Data.Repository.Includes/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml index 8638dea7..0ce35b2f 100644 --- a/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Repository.Includes IMAGE_NAME: api.data.repository.includes SERVICE_NAME: api-data-repository-includes VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml b/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Repository.Includes/.kubernetes/configmap.yaml b/Api.Data.Repository.Includes/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Repository.Includes/.kubernetes/configmap.yaml +++ b/Api.Data.Repository.Includes/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Repository.Includes/.kubernetes/deployment.yaml b/Api.Data.Repository.Includes/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.Repository.Includes/.kubernetes/deployment.yaml +++ b/Api.Data.Repository.Includes/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local index f67686a8..d346cc6b 100644 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Repository.Includes.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Dockerfile b/Api.Data.Repository.Includes/Dockerfile index 51c6edab..25101994 100644 --- a/Api.Data.Repository.Includes/Dockerfile +++ b/Api.Data.Repository.Includes/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.SoftDelete/.docker/docker-compose.yml b/Api.Data.SoftDelete/.docker/docker-compose.yml index 2182ed3f..0fbddac4 100644 --- a/Api.Data.SoftDelete/.docker/docker-compose.yml +++ b/Api.Data.SoftDelete/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml b/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml index 957cec57..8de44e0c 100644 --- a/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml +++ b/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.SoftDelete IMAGE_NAME: api.data.softdelete SERVICE_NAME: api-data-softdelete VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml b/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.SoftDelete/.kubernetes/configmap.yaml b/Api.Data.SoftDelete/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.SoftDelete/.kubernetes/configmap.yaml +++ b/Api.Data.SoftDelete/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.SoftDelete/.kubernetes/deployment.yaml b/Api.Data.SoftDelete/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.SoftDelete/.kubernetes/deployment.yaml +++ b/Api.Data.SoftDelete/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local b/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local index f0398733..00faa108 100644 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.SoftDelete.dll"] \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.SoftDelete/Dockerfile b/Api.Data.SoftDelete/Dockerfile index 4c606a9e..a494a421 100644 --- a/Api.Data.SoftDelete/Dockerfile +++ b/Api.Data.SoftDelete/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.SqLite/.github/workflows/build-and-deploy.yml b/Api.Data.SqLite/.github/workflows/build-and-deploy.yml index 98ad1455..3e521bd1 100644 --- a/Api.Data.SqLite/.github/workflows/build-and-deploy.yml +++ b/Api.Data.SqLite/.github/workflows/build-and-deploy.yml @@ -2,33 +2,30 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main env: APP_NAME: Api.Data.SqLite IMAGE_NAME: api.data.sqlite SERVICE_NAME: api-data-sqlite VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,23 +33,20 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - SQL_NAME: nanoDb + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_SIZE: 10Gi - SQL_MIGRATION_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.SQL_NAME }}.sqlite" +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -65,80 +59,64 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - name: Publish NuGet shell: pwsh run: | $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Database Migration + - name: Build & Push Image shell: pwsh run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - if ($LastExitCode -ne 0) + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + + if ($LastExitCode -ne 0) { throw "error"; - }; + }; + + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; - name: Kubernetes Deploy shell: pwsh @@ -186,7 +164,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -194,6 +172,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.SqLite/.kubernetes/configmap.yaml b/Api.Data.SqLite/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.SqLite/.kubernetes/configmap.yaml +++ b/Api.Data.SqLite/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.SqLite/.kubernetes/data-pvc.yaml b/Api.Data.SqLite/.kubernetes/data-pvc.yaml index 0b9d4415..00bca640 100644 --- a/Api.Data.SqLite/.kubernetes/data-pvc.yaml +++ b/Api.Data.SqLite/.kubernetes/data-pvc.yaml @@ -8,4 +8,4 @@ spec: storageClassName: %SERVICE_NAME%-data-storage-class resources: requests: - storage: %DATA_SIZE% \ No newline at end of file + storage: %SQL_SIZE% \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/data-storageclass.yaml b/Api.Data.SqLite/.kubernetes/data-storageclass.yaml index 47530cdc..2f66693c 100644 --- a/Api.Data.SqLite/.kubernetes/data-storageclass.yaml +++ b/Api.Data.SqLite/.kubernetes/data-storageclass.yaml @@ -2,7 +2,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: %SERVICE_NAME%-data-storage-class -provisioner: kubernetes.io/azure-disk +provisioner: disk.csi.azure.com parameters: storageaccounttype: Standard_LRS kind: Managed diff --git a/Api.Data.SqLite/.kubernetes/deployment.yaml b/Api.Data.SqLite/.kubernetes/deployment.yaml index 354c85fb..fd8b1951 100644 --- a/Api.Data.SqLite/.kubernetes/deployment.yaml +++ b/Api.Data.SqLite/.kubernetes/deployment.yaml @@ -80,6 +80,3 @@ spec: - name: %SERVICE_NAME%-volume persistentVolumeClaim: claimName: %SERVICE_NAME%-pvc - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj b/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj index 796675d7..4fd03943 100644 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local b/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local index 28ad6e11..0dd334f1 100644 --- a/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local +++ b/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.SqLite.dll"] \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json index 8927ff99..29091fa5 100644 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json @@ -1,5 +1,3 @@ { - "Data": { - "StartupAction": "Migrate" - } + } \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.json index 496df4fb..8e51eb81 100644 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.json +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.json @@ -20,7 +20,7 @@ "BulkBatchSize": 500, "BulkBatchDelay": 1000, "QueryRetryCount": 0, - "StartupAction": "None", + "StartupAction": "Migrate", "UseLazyLoading": false, "UseSensitiveDataLogging": false, "QuerySplittingBehavior": "SingleQuery", diff --git a/Api.Data.SqLite/Dockerfile b/Api.Data.SqLite/Dockerfile index 8c1d942b..aa01ca87 100644 --- a/Api.Data.SqLite/Dockerfile +++ b/Api.Data.SqLite/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.SqLite/README.md b/Api.Data.SqLite/README.md index 0884a1cc..9aef9986 100644 --- a/Api.Data.SqLite/README.md +++ b/Api.Data.SqLite/README.md @@ -81,7 +81,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": "Data Source=/data/nanoDb.sqlite", "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, @@ -92,13 +92,7 @@ Configured the application with the necessary data setup. } ``` -...and `appsettings.Development.json` - -```json -"Data": { - "StartupAction": "Migrate" -} -``` +> ⚠️ **Notice:** `StartupAction` is set to `migrate` in all environments. This may cause the pod to restart during the initial deployment. ## Docker Compose Added SqLite as a service dependency in `docker-compose.yml`. @@ -134,34 +128,7 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: - SQL_NAME: nanoDb SQL_SIZE: 10Gi - SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.nanoDb }}.sqlite" -``` - -Additionally, this step has been added to ensure database migrations are applied. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; ``` Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml index e7532a1d..8922d769 100644 --- a/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml +++ b/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.SqlServer.Spatial IMAGE_NAME: api.data.sqlserver.spatial SERVICE_NAME: api-data-sqlserver-spatial VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-sqlserver-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,80 +65,80 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "1433" $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -153,7 +152,7 @@ jobs: -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -h -1 ` -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" @@ -163,7 +162,7 @@ jobs: -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" }; @@ -231,7 +230,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -239,6 +238,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml +++ b/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml +++ b/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj index 8c31e7ba..396d24b3 100644 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local index cb81de7f..8b5fa640 100644 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.SqlServer.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Dockerfile b/Api.Data.SqlServer.Spatial/Dockerfile index 1bc24530..e05b9d4b 100644 --- a/Api.Data.SqlServer.Spatial/Dockerfile +++ b/Api.Data.SqlServer.Spatial/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml index 370b8153..20874458 100644 --- a/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml +++ b/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.SqlServer IMAGE_NAME: api.data.sqlserver SERVICE_NAME: api-data-sqlserver VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-sqlserver-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,80 +65,80 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "1433" $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -153,7 +152,7 @@ jobs: -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -h -1 ` -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" @@ -163,7 +162,7 @@ jobs: -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" }; @@ -231,7 +230,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -239,6 +238,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml b/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.SqlServer/.kubernetes/configmap.yaml b/Api.Data.SqlServer/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.SqlServer/.kubernetes/configmap.yaml +++ b/Api.Data.SqlServer/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.SqlServer/.kubernetes/deployment.yaml b/Api.Data.SqlServer/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.SqlServer/.kubernetes/deployment.yaml +++ b/Api.Data.SqlServer/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj index 8c31e7ba..396d24b3 100644 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local b/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local index 8297641c..4cae4ffe 100644 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer/Dockerfile b/Api.Data.SqlServer/Dockerfile index e140c4f8..d260d7a1 100644 --- a/Api.Data.SqlServer/Dockerfile +++ b/Api.Data.SqlServer/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Data.SqlServer/README.md b/Api.Data.SqlServer/README.md index c1286d17..4bfaafca 100644 --- a/Api.Data.SqlServer/README.md +++ b/Api.Data.SqlServer/README.md @@ -81,7 +81,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": null, "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, @@ -123,7 +123,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `deployment.yaml`. ```json spec: @@ -143,6 +143,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-sqlserver-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -158,12 +160,15 @@ Additionally, this step has been added to ensure database migrations are applied $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "1433" $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -174,50 +179,51 @@ Additionally, this step has been added to ensure database migrations are applied apt-get install -y mssql-tools unixodbc-dev $loginExists = sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d main ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" if ($loginExists -eq 0) { sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d main ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" }; $userExists = sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d $env:SQL_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" if ($userExists -eq 0) { sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d $env:SQL_NAME ` - -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" }; + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; ``` -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. +Last, before applying the new Kubernetes templates, these environmental variables must be set. -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; +```powershell +$env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_USER;Password=$env:SQL_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; ``` + +Finally, the templates can be applied. diff --git a/Api.Data.Triggers/.docker/docker-compose.yml b/Api.Data.Triggers/.docker/docker-compose.yml index 1bd3d4c3..b7e371df 100644 --- a/Api.Data.Triggers/.docker/docker-compose.yml +++ b/Api.Data.Triggers/.docker/docker-compose.yml @@ -20,11 +20,8 @@ services: networks: - network environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb MYSQL_ROOT_HOST: '%' + MYSQL_ROOT_PASSWORD: myPassword_123 networks: network: diff --git a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml index 0cbf7483..ba28c013 100644 --- a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml +++ b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Data.Triggers IMAGE_NAME: api.data.triggers SERVICE_NAME: api-data-triggers VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_KUBERNETES_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,24 +36,23 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,86 +65,88 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -174,7 +175,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -212,7 +213,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -220,6 +221,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml b/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml +++ b/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Api.Data.Triggers/.kubernetes/configmap.yaml b/Api.Data.Triggers/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Data.Triggers/.kubernetes/configmap.yaml +++ b/Api.Data.Triggers/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Data.Triggers/.kubernetes/deployment.yaml b/Api.Data.Triggers/.kubernetes/deployment.yaml index 7e8688ef..a6f04b0f 100644 --- a/Api.Data.Triggers/.kubernetes/deployment.yaml +++ b/Api.Data.Triggers/.kubernetes/deployment.yaml @@ -79,6 +79,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj b/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj index 82be9b94..8fc5868b 100644 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local b/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local index b8b7fc64..72600b85 100644 --- a/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local +++ b/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Data.Triggers.dll"] \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json +++ b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Api.Data.Triggers/Dockerfile b/Api.Data.Triggers/Dockerfile index 6f4ffa68..0225e47b 100644 --- a/Api.Data.Triggers/Dockerfile +++ b/Api.Data.Triggers/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml index 00b3b84b..10b03dd2 100644 --- a/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml +++ b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml @@ -2,35 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Documentation.Csp IMAGE_NAME: api.documentation.csp SERVICE_NAME: api-documentation-csp SUB_DOMAIN_NAME: nano VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,20 +36,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,71 +61,70 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | + $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query [].name; $env:ROUTE_HOST_NAMES = @( @@ -139,8 +135,6 @@ jobs: } ) -join "`n"; - $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; @@ -173,7 +167,7 @@ jobs: } - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -181,6 +175,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Documentation.Csp/.kubernetes/deployment.yaml b/Api.Documentation.Csp/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Documentation.Csp/.kubernetes/deployment.yaml +++ b/Api.Documentation.Csp/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj b/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj index 7a4d41f3..5721d8ef 100644 --- a/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj +++ b/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local b/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local index 8f4d7cbf..69031946 100644 --- a/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 4443 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Documentation.Csp.dll"] \ No newline at end of file diff --git a/Api.Documentation.Csp/Dockerfile b/Api.Documentation.Csp/Dockerfile index 32d43b6b..145a982f 100644 --- a/Api.Documentation.Csp/Dockerfile +++ b/Api.Documentation.Csp/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Documentation/.github/workflows/build-and-deploy.yml b/Api.Documentation/.github/workflows/build-and-deploy.yml index 87545d98..8c9d47f6 100644 --- a/Api.Documentation/.github/workflows/build-and-deploy.yml +++ b/Api.Documentation/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Documentation IMAGE_NAME: api.documentation SERVICE_NAME: api-documentation VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: - build-and-deploy: + build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Documentation/.kubernetes/configmap.yaml b/Api.Documentation/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Documentation/.kubernetes/configmap.yaml +++ b/Api.Documentation/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Documentation/.kubernetes/deployment.yaml b/Api.Documentation/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Documentation/.kubernetes/deployment.yaml +++ b/Api.Documentation/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj b/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj +++ b/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Documentation/Api.Documentation/Dockerfile.Local b/Api.Documentation/Api.Documentation/Dockerfile.Local index 8304fa2a..29c326a8 100644 --- a/Api.Documentation/Api.Documentation/Dockerfile.Local +++ b/Api.Documentation/Api.Documentation/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Documentation.dll"] \ No newline at end of file diff --git a/Api.Documentation/Dockerfile b/Api.Documentation/Dockerfile index bc41ebbf..d928e41b 100644 --- a/Api.Documentation/Dockerfile +++ b/Api.Documentation/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Documentation/README.md b/Api.Documentation/README.md index 49195e45..1f878977 100644 --- a/Api.Documentation/README.md +++ b/Api.Documentation/README.md @@ -56,4 +56,3 @@ for the same example endpoint. When set to `true`, Swagger only displays the non } } ``` - diff --git a/Api.ErrorHandling/.github/workflows/build-and-deploy.yml b/Api.ErrorHandling/.github/workflows/build-and-deploy.yml index 012b1be2..d5b1a272 100644 --- a/Api.ErrorHandling/.github/workflows/build-and-deploy.yml +++ b/Api.ErrorHandling/.github/workflows/build-and-deploy.yml @@ -6,14 +6,16 @@ on: push: branches: - master + workflow_dispatch: env: APP_NAME: Api.ErrorHandling IMAGE_NAME: api.errorhandling SERVICE_NAME: api-errorhandling VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} @@ -21,10 +23,6 @@ env: NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu KUBERNETES_NAMESPACE: apps KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} @@ -37,6 +35,9 @@ env: KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: @@ -46,10 +47,6 @@ jobs: permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ErrorHandling/.kubernetes/configmap.yaml b/Api.ErrorHandling/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ErrorHandling/.kubernetes/configmap.yaml +++ b/Api.ErrorHandling/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ErrorHandling/.kubernetes/deployment.yaml b/Api.ErrorHandling/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ErrorHandling/.kubernetes/deployment.yaml +++ b/Api.ErrorHandling/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj b/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj +++ b/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local b/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local index 77abb0b3..c4679092 100644 --- a/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local +++ b/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ErrorHandling.dll"] \ No newline at end of file diff --git a/Api.ErrorHandling/Dockerfile b/Api.ErrorHandling/Dockerfile index de88ee4d..621e601b 100644 --- a/Api.ErrorHandling/Dockerfile +++ b/Api.ErrorHandling/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml index e4a92c54..db8f69b2 100644 --- a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml +++ b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Eventing.RabbitMq IMAGE_NAME: api.eventing.rabbitmq SERVICE_NAME: api.eventing-rabbitmq VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml b/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml +++ b/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml index c8e22634..5a30d851 100644 --- a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml +++ b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml @@ -38,16 +38,25 @@ spec: - containerPort: 8080 imagePullPolicy: Always env: + - name: Eventing__Credentials__Host + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: host + - name: Eventing__Credentials__Port + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: port - name: Eventing__Credentials__Id valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: username - envFrom: - name: Eventing__Credentials__Secret valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: password envFrom: - configMapRef: @@ -85,6 +94,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj index 04328227..0a22c469 100644 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local index e183367d..7efce886 100644 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Dockerfile b/Api.Eventing.RabbitMq/Dockerfile index 4775d14f..487adf44 100644 --- a/Api.Eventing.RabbitMq/Dockerfile +++ b/Api.Eventing.RabbitMq/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Eventing.RabbitMq/README.md b/Api.Eventing.RabbitMq/README.md index 853d3e09..9b00625a 100644 --- a/Api.Eventing.RabbitMq/README.md +++ b/Api.Eventing.RabbitMq/README.md @@ -72,8 +72,8 @@ Configured the application `appsettings.json` with the necessary eventing setup. "Heartbeat": 60, "PrefetchCount": 50, "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" + "Id": null, + "Secret": null }, "HealthCheck": { "UnhealthyStatus": "Unhealthy" @@ -129,23 +129,32 @@ services: ## Kubernetes Added the `rabbitmq` secret for password to the `deployment.yaml`. -```json +```yaml spec: template: spec: containers: env: + - name: Eventing__Credentials__Host + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: host + - name: Eventing__Credentials__Port + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: port - name: Eventing__Credentials__Id valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: username - envFrom: - name: Eventing__Credentials__Secret valueFrom: secretKeyRef: - name: rabbitmq-auth + name: rabbitmq-default-user key: password ``` -> ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** +> ⚠️ The `rabbitmq` secret is created alongside the **[Nano.Azure.Kubernetes.RabbitMQ](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Api.HealthChecks/.docker/docker-compose.yml b/Api.HealthChecks/.docker/docker-compose.yml index dc34bde1..550a4254 100644 --- a/Api.HealthChecks/.docker/docker-compose.yml +++ b/Api.HealthChecks/.docker/docker-compose.yml @@ -5,9 +5,12 @@ services: restart: on-failure ports: - 8080:8080 + - 4443:4443 build: context: ../Api.HealthChecks dockerfile: "Dockerfile.Local" + volumes: + - ../:/root/.dotnet/https networks: - network diff --git a/Api.HealthChecks/.github/workflows/build-and-deploy.yml b/Api.HealthChecks/.github/workflows/build-and-deploy.yml index e09d6b24..67e919e4 100644 --- a/Api.HealthChecks/.github/workflows/build-and-deploy.yml +++ b/Api.HealthChecks/.github/workflows/build-and-deploy.yml @@ -2,33 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.HealthChecks IMAGE_NAME: api.healthchecks SERVICE_NAME: api-healthchecks VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} + AZURE_GROUP_LOGS: ${{ vars.AZURE_RESOURCE_GROUP_LOGS }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,22 +36,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} - AVAILABILITY_CHECK_FREQUENCY: 300 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,134 +61,78 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Add Availability Check - id: add-availability-check + - name: Publish NuGet shell: pwsh run: | - az extension add -n application-insights; - - $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; - $env:APPLICATION_INSIGHT_ID = az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) - { - $env:WORKSPACE_ID = az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; - - if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) - { - $env:APPLICATION_INSIGHT_ID = az monitor app-insights component create ` - -a $env:SERVICE_NAME_INSIGTHS ` - -l $env:AZURE_LOCATION ` - -g $env:AZURE_GROUP ` - --workspace $env:WORKSPACE_ID ` - --query "[id]" -o tsv; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - }; - - $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); - $env:AVAILABILITY_ID = az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) - { - $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; - az monitor app-insights web-test create ` - -n $env:SERVICE_NAME_AVAILABILITY ` - --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` - -g $env:AZURE_GROUP ` - -l $env:AZURE_LOCATION ` - --kind multistep ` - --web-test-kind standard ` - --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` - --enabled true ` - --retry-enabled true ` - --ssl-check true ` - --ssl-lifetime-check 30 ` - --http-verb GET ` - --request-url $env:AVAILABILITY_URI ` - --expected-status-code 200 ` - --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` - --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` - --locations Id='us-ca-sjc-azr' ` - --locations Id='us-va-ash-azr' ` - --locations Id='emea-gb-db3-azr' ` - --locations Id='emea-nl-ams-azr' ` - --locations Id='apac-hk-hkn-azr'; - }; - if ($LastExitCode -ne 0) + if ($LastExitCode -ne 0) { throw "error"; - }; + }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + ./ - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - if ($LastExitCode -ne 0) - { - throw "error"; - }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; - name: Kubernetes Deploy shell: pwsh run: | + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + $env:ROUTE_HOST_NAMES = ( + $zoneNames | ForEach-Object { + " - $env:SUB_DOMAIN_NAME.$_" + } + ) -join "`n" + + $env:GATEWAY_NAME = kubectl get gateway -n $env:KUBERNETES_NAMESPACE -o jsonpath='{.items[0].metadata.name}' + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; kubectl apply -f .kubernetes/service.tmp.yaml; if ($LastExitCode -ne 0) @@ -220,8 +161,111 @@ jobs: throw "error"; }; + Get-Content .kubernetes/httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/httproute-80.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/httproute-443.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + - name: Add Availability Check + shell: pwsh + run: | + $env:AZURE_LOCATION = az monitor log-analytics workspace list -g $env:AZURE_GROUP_LOGS --query [0].location -o tsv; + $env:APPLICATION_INSIGHT_ID = az monitor app-insights component show -g $env:AZURE_GROUP_LOGS --query [0].id -o tsv; + $env:HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; + + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + foreach ($zoneName in $zoneNames) + { + $env:WEB_TEST_NAME = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower() + '-' + $env:SUB_DOMAIN_NAME + '-' + ($zoneName.TrimEnd('.') -replace '\.', '-') + + az monitor app-insights web-test show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_NAME --query id -o tsv 2>$null + + if ($LastExitCode -ne 0) + { + az monitor app-insights web-test create ` + -n $env:WEB_TEST_NAME ` + --defined-web-test-name $env:WEB_TEST_NAME ` + -g $env:AZURE_GROUP_LOGS ` + -l $env:AZURE_LOCATION ` + --kind ping ` + --web-test-kind standard ` + --frequency 300 ` + --enabled true ` + --retry-enabled true ` + --ssl-check true ` + --ssl-lifetime-check 30 ` + --http-verb GET ` + --request-url https://$env:SUB_DOMAIN_NAME.$zoneName/healthz ` + --expected-status-code 200 ` + --content-validation content-match='"status":"unhealthy"' ignore-case=true pass-if-text-found=false ` + --tags $env:HIDDEN_LINK ` + --locations Id='us-ca-sjc-azr' ` + --locations Id='us-va-ash-azr' ` + --locations Id='emea-gb-db3-azr' ` + --locations Id='emea-nl-ams-azr' ` + --locations Id='apac-hk-hkn-azr'; + + if ($LastExitCode -ne 0) + { + throw "error"; + } + } + + $env:WEB_TEST_ALERT_NAME = $env:WEB_TEST_NAME + "-alert"; + + az resource show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_ALERT_NAME --query id -o tsv 2>$null; + + if ($LastExitCode -ne 0) + { + $env:WEB_TEST_ID = az monitor app-insights web-test show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_NAME --query id -o tsv; + $env:ACTION_GROUP_ID = az monitor action-group list -g $env:AZURE_GROUP_LOGS --query [0].id -o tsv; + + $alertRuleProperties = @{ + severity = 1 + enabled = $true + scopes = @($env:WEB_TEST_ID, $env:APPLICATION_INSIGHT_ID) + evaluationFrequency = "PT1M" + windowSize = "PT5M" + criteria = @{ + "odata.type" = "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + webTestId = $env:WEB_TEST_ID + componentId = $env:APPLICATION_INSIGHT_ID + failedLocationCount = 2 + } + actions = @( + @{ actionGroupId = $env:ACTION_GROUP_ID } + ) + } + + $json = $alertRuleProperties | ConvertTo-Json -Depth 10 + [System.IO.File]::WriteAllText("$PWD/alert.json", $json, [System.Text.UTF8Encoding]::new($false)) + + az resource create ` + -g $env:AZURE_GROUP_LOGS ` + -n $env:WEB_TEST_ALERT_NAME ` + -l global ` + --resource-type "Microsoft.Insights/metricAlerts" ` + -p '@alert.json'; + + if ($LastExitCode -ne 0) + { + throw "error"; + } + } + } + - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -229,6 +273,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.HealthChecks/.kubernetes/configmap.yaml b/Api.HealthChecks/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.HealthChecks/.kubernetes/configmap.yaml +++ b/Api.HealthChecks/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.HealthChecks/.kubernetes/deployment.yaml b/Api.HealthChecks/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.HealthChecks/.kubernetes/deployment.yaml +++ b/Api.HealthChecks/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.HealthChecks/.kubernetes/httproute-443.yaml b/Api.HealthChecks/.kubernetes/httproute-443.yaml new file mode 100644 index 00000000..25060955 --- /dev/null +++ b/Api.HealthChecks/.kubernetes/httproute-443.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route-443 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + hostnames: +%ROUTE_HOST_NAMES% + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: %SERVICE_NAME% + port: 8080 \ No newline at end of file diff --git a/Api.HealthChecks/.kubernetes/httproute-80.yaml b/Api.HealthChecks/.kubernetes/httproute-80.yaml new file mode 100644 index 00000000..e743bb46 --- /dev/null +++ b/Api.HealthChecks/.kubernetes/httproute-80.yaml @@ -0,0 +1,17 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route-80 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + sectionName: http + hostnames: +%ROUTE_HOST_NAMES% + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj b/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj +++ b/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.HealthChecks/Api.HealthChecks.sln b/Api.HealthChecks/Api.HealthChecks.sln index 50d5122c..b0f55d32 100644 --- a/Api.HealthChecks/Api.HealthChecks.sln +++ b/Api.HealthChecks/Api.HealthChecks.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{ Dockerfile = Dockerfile icon.png = icon.png LICENSE = LICENSE + localhost.pfx = localhost.pfx README.md = README.md EndProjectSection EndProject @@ -22,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes" .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml .kubernetes\configmap.yaml = .kubernetes\configmap.yaml .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\httproute-443.yaml = .kubernetes\httproute-443.yaml + .kubernetes\httproute-80.yaml = .kubernetes\httproute-80.yaml .kubernetes\service.yaml = .kubernetes\service.yaml EndProjectSection EndProject diff --git a/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local b/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local index 927b61e9..896d1208 100644 --- a/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local +++ b/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.HealthChecks.dll"] \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json index 8593c62d..c298b7e5 100644 --- a/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json +++ b/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json @@ -1,2 +1,19 @@ { + "App": { + "Hosting": { + "Http": { + "UseHttpsRedirection": true + }, + "Https": { + "Ports": [ + 4443 + ], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + }, + "UseHttpsRequired": true + } + } + } } \ No newline at end of file diff --git a/Api.HealthChecks/Dockerfile b/Api.HealthChecks/Dockerfile index 2e170ee4..2dfeea28 100644 --- a/Api.HealthChecks/Dockerfile +++ b/Api.HealthChecks/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.HealthChecks/README.md b/Api.HealthChecks/README.md index 18536976..0720ecca 100644 --- a/Api.HealthChecks/README.md +++ b/Api.HealthChecks/README.md @@ -18,7 +18,7 @@ Nano is referenced directly from source (not via NuGet packages) and is expected * [GitHub Actions](#gitHub-actions) ## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)** and adds a simple test controller that inherits from the top-level Nano `BaseController`. This example illustrates the use of Nano API health-checks. @@ -46,79 +46,104 @@ There is no configuration for HealtCheck, the section has just been added to ena ## GitHub Actions Optionally, you can configure an availability check in Azure using Application Insights to continuously monitor your application's health and responsiveness. -> ⚠️ This requires the application to be exposed publicly from Kubernetes via an `ingress` configuration. +> ⚠️ This requires the application to be exposed publicly from Kubernetes via an `httproute` configuration. -Add the following environment variables. +First, add the following environment variables. ```yaml env: - AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} - AVAILABILITY_CHECK_FREQUENCY: 300 + AZURE_GROUP_LOGS: ${{ vars.AZURE_RESOURCE_GROUP_LOGS }} ``` -...and then add the `Add Availability Check` step to the action pipeline. +And then the Availability Check step to the end of the pipeline. ```yaml - - name: Add Availability Check - id: add-availability-check - shell: pwsh - run: | - sudo az extension add -n application-insights; - - $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) - { - $env:WORKSPACE_ID = sudo az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; - - if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) - { - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component create ` - -a $env:SERVICE_NAME_INSIGTHS ` - -l $env:AZURE_LOCATION ` - -g $env:AZURE_GROUP ` - --workspace $env:WORKSPACE_ID ` - --query "[id]" -o tsv; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - }; - - $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); - $env:AVAILABILITY_ID = sudo az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) - { - $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; - sudo az monitor app-insights web-test create ` - -n $env:SERVICE_NAME_AVAILABILITY ` - --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` - -g $env:AZURE_GROUP ` - -l $env:AZURE_LOCATION ` - --kind multistep ` - --web-test-kind standard ` - --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` - --enabled true ` - --retry-enabled true ` - --ssl-check true ` - --ssl-lifetime-check 30 ` - --http-verb GET ` - --request-url $env:AVAILABILITY_URI ` - --expected-status-code 200 ` - --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` - --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` - --locations Id='us-ca-sjc-azr' ` - --locations Id='us-va-ash-azr' ` - --locations Id='emea-gb-db3-azr' ` - --locations Id='emea-nl-ams-azr' ` - --locations Id='apac-hk-hkn-azr'; - }; - if ($LastExitCode -ne 0) - { - throw "error"; - }; +- name: Add Availability Check + shell: pwsh + run: | + $env:AZURE_LOCATION = az monitor log-analytics workspace list -g $env:AZURE_GROUP_LOGS --query [0].location -o tsv; + $env:APPLICATION_INSIGHT_ID = az monitor app-insights component show -g $env:AZURE_GROUP_LOGS --query [0].id -o tsv; + $env:HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; + + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + foreach ($zoneName in $zoneNames) + { + $env:WEB_TEST_NAME = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower() + '-' + $env:SUB_DOMAIN_NAME + '-' + ($zoneName.TrimEnd('.') -replace '\.', '-') + + az monitor app-insights web-test show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_NAME --query id -o tsv 2>$null + + if ($LastExitCode -ne 0) + { + az monitor app-insights web-test create ` + -n $env:WEB_TEST_NAME ` + --defined-web-test-name $env:WEB_TEST_NAME ` + -g $env:AZURE_GROUP_LOGS ` + -l $env:AZURE_LOCATION ` + --kind ping ` + --web-test-kind standard ` + --frequency 300 ` + --enabled true ` + --retry-enabled true ` + --ssl-check true ` + --ssl-lifetime-check 30 ` + --http-verb GET ` + --request-url https://$env:SUB_DOMAIN_NAME.$zoneName/healthz ` + --expected-status-code 200 ` + --content-validation content-match='"status":"unhealthy"' ignore-case=true pass-if-text-found=false ` + --tags $env:HIDDEN_LINK ` + --locations Id='us-ca-sjc-azr' ` + --locations Id='us-va-ash-azr' ` + --locations Id='emea-gb-db3-azr' ` + --locations Id='emea-nl-ams-azr' ` + --locations Id='apac-hk-hkn-azr'; + + if ($LastExitCode -ne 0) + { + throw "error"; + } + } + + $env:WEB_TEST_ALERT_NAME = $env:WEB_TEST_NAME + "-alert"; + + az resource show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_ALERT_NAME --query id -o tsv 2>$null; + + if ($LastExitCode -ne 0) + { + $env:WEB_TEST_ID = az monitor app-insights web-test show -g $env:AZURE_GROUP_LOGS -n $env:WEB_TEST_NAME --query id -o tsv; + $env:ACTION_GROUP_ID = az monitor action-group list -g $env:AZURE_GROUP_LOGS --query [0].id -o tsv; + + $alertRuleProperties = @{ + severity = 1 + enabled = $true + scopes = @($env:WEB_TEST_ID, $env:APPLICATION_INSIGHT_ID) + evaluationFrequency = "PT1M" + windowSize = "PT5M" + criteria = @{ + "odata.type" = "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + webTestId = $env:WEB_TEST_ID + componentId = $env:APPLICATION_INSIGHT_ID + failedLocationCount = 2 + } + actions = @( + @{ actionGroupId = $env:ACTION_GROUP_ID } + ) + } + + $json = $alertRuleProperties | ConvertTo-Json -Depth 10 + [System.IO.File]::WriteAllText("$PWD/alert.json", $json, [System.Text.UTF8Encoding]::new($false)) + + az resource create ` + -g $env:AZURE_GROUP_LOGS ` + -n $env:WEB_TEST_ALERT_NAME ` + -l global ` + --resource-type "Microsoft.Insights/metricAlerts" ` + -p '@alert.json'; + + if ($LastExitCode -ne 0) + { + throw "error"; + } + } + } ``` diff --git a/Api.HealthChecks/localhost.pfx b/Api.HealthChecks/localhost.pfx new file mode 100644 index 00000000..22e513ca Binary files /dev/null and b/Api.HealthChecks/localhost.pfx differ diff --git a/Api.Hosting.Http/.github/workflows/build-and-deploy.yml b/Api.Hosting.Http/.github/workflows/build-and-deploy.yml index 730be7cd..f1f9386e 100644 --- a/Api.Hosting.Http/.github/workflows/build-and-deploy.yml +++ b/Api.Hosting.Http/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Hosting.Http IMAGE_NAME: api.hosting.http SERVICE_NAME: api-hosting-http VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Hosting.Http/.kubernetes/configmap.yaml b/Api.Hosting.Http/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Hosting.Http/.kubernetes/configmap.yaml +++ b/Api.Hosting.Http/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Hosting.Http/.kubernetes/deployment.yaml b/Api.Hosting.Http/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Hosting.Http/.kubernetes/deployment.yaml +++ b/Api.Hosting.Http/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj b/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj +++ b/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local b/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local index 272c1ffb..3a443213 100644 --- a/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local +++ b/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Hosting.Http.dll"] \ No newline at end of file diff --git a/Api.Hosting.Http/Dockerfile b/Api.Hosting.Http/Dockerfile index 2d614ac6..b5601062 100644 --- a/Api.Hosting.Http/Dockerfile +++ b/Api.Hosting.Http/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Hosting.Https/.github/workflows/build-and-deploy.yml b/Api.Hosting.Https/.github/workflows/build-and-deploy.yml index 629d9c6f..d0b4ac6a 100644 --- a/Api.Hosting.Https/.github/workflows/build-and-deploy.yml +++ b/Api.Hosting.Https/.github/workflows/build-and-deploy.yml @@ -2,35 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Hosting.Https IMAGE_NAME: api.hosting.https SERVICE_NAME: api-hosting-https SUB_DOMAIN_NAME: nano VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,20 +36,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -137,7 +131,7 @@ jobs: } ) -join "`n" - $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + $env:GATEWAY_NAME = kubectl get gateway -n $env:KUBERNETES_NAMESPACE -o jsonpath='{.items[0].metadata.name}' Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; kubectl apply -f .kubernetes/service.tmp.yaml; @@ -167,22 +161,22 @@ jobs: throw "error"; } - Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + Get-Content .kubernetes/httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/httproute-80.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + Get-Content .kubernetes/httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/httproute-443.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -190,6 +184,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Hosting.Https/.kubernetes/configmap.yaml b/Api.Hosting.Https/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Hosting.Https/.kubernetes/configmap.yaml +++ b/Api.Hosting.Https/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Hosting.Https/.kubernetes/deployment.yaml b/Api.Hosting.Https/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Hosting.Https/.kubernetes/deployment.yaml +++ b/Api.Hosting.Https/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.Https/.kubernetes/httproute-443.yaml b/Api.Hosting.Https/.kubernetes/httproute-443.yaml index 65b97a51..25060955 100644 --- a/Api.Hosting.Https/.kubernetes/httproute-443.yaml +++ b/Api.Hosting.Https/.kubernetes/httproute-443.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %SERVICE_NAME%-route + name: %SERVICE_NAME%-route-443 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.Hosting.Https/.kubernetes/httproute-80.yaml b/Api.Hosting.Https/.kubernetes/httproute-80.yaml index f29775aa..e743bb46 100644 --- a/Api.Hosting.Https/.kubernetes/httproute-80.yaml +++ b/Api.Hosting.Https/.kubernetes/httproute-80.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %APP_NAME%-route-80 + name: %SERVICE_NAME%-route-80 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj b/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj index 7a4d41f3..5721d8ef 100644 --- a/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj +++ b/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local b/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local index e15900e2..c0b93077 100644 --- a/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local +++ b/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 4443 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Hosting.Https.dll"] \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json index 23bfb871..c298b7e5 100644 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json @@ -1,7 +1,7 @@ { "App": { "Hosting": { - "http": { + "Http": { "UseHttpsRedirection": true }, "Https": { diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.json index e8d9ff25..31aeb899 100644 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.json +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.json @@ -6,7 +6,8 @@ "Http": { "Ports": [ 8080 - ] + ], + "UseHttpsRedirection": false } } } diff --git a/Api.Hosting.Https/Dockerfile b/Api.Hosting.Https/Dockerfile index ed3c969f..c001d061 100644 --- a/Api.Hosting.Https/Dockerfile +++ b/Api.Hosting.Https/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Hosting.Https/README.md b/Api.Hosting.Https/README.md index b9bf791d..37598f0e 100644 --- a/Api.Hosting.Https/README.md +++ b/Api.Hosting.Https/README.md @@ -43,6 +43,24 @@ HTTPS and the development certificate are only configured in `appsettings.Develo in Kubernetes. Certificates are managed externally, and the service only needs to expose an HTTP port in `service.yaml` for mapping traffic through the ingress, which serves HTTPS and forwards it to HTTP. +The `appsettings.json` has this configuration. + +```json +"App": { + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ], + "UseHttpsRedirection": false + } + } +} +``` + +and the `appsettings.Development.json` this. + ```json "App": { "Hosting": { @@ -76,19 +94,72 @@ services: ``` ## Kubernetes -A `httproute.yaml` resource has been added to the `.kubernetes` folder. +A `httproute-80.yaml` and `httproute-443.yaml` resource has been added. -| File / Directory | Type | Description | -| -------------------- | ------- | -------------------------------------------- | -| `httproute.yaml` | `yaml` | The http route spec for Kubernetes Gateway. | +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route-80 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + sectionName: http + hostnames: +%ROUTE_HOST_NAMES% + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + +``` + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route-443 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + hostnames: +%ROUTE_HOST_NAMES% + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: %SERVICE_NAME% + port: 8080 +``` ## GitHub Actions -Deployment commands have been updated to apply the new Kubernetes `HTTPRoute` template. +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SUB_DOMAIN_NAME: papi + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} +``` + +Additionally, during the Kubernetes deployment step, before any resources are applied, all DNS zones are iterated, and the hostname configured in the `httpRoutes` resource is updated to +include the application's subdomain. ```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; +$zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +$env:ROUTE_HOST_NAMES = ( + $zoneNames | ForEach-Object { + " - $env:SUB_DOMAIN_NAME.$_" + } +) -join "`n" + +$env:GATEWAY_NAME = kubectl get gateway -n $env:KUBERNETES_NAMESPACE -o jsonpath='{.items[0].metadata.name}' ``` + +The deployment commands have been updated to apply the new Kubernetes `HTTPRoute` templates. diff --git a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml index 4ca38002..c6283627 100644 --- a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml +++ b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Hosting.MultipartLimits IMAGE_NAME: api.hosting.multipartlimits SERVICE_NAME: api-Hosting-multipartlimits VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml b/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml +++ b/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml b/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml +++ b/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local index e80d58e4..81a4ac08 100644 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Hosting.MultipartLimits.dll"] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Dockerfile b/Api.Hosting.MultipartLimits/Dockerfile index b205db82..8c3a1f9a 100644 --- a/Api.Hosting.MultipartLimits/Dockerfile +++ b/Api.Hosting.MultipartLimits/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Localization/.github/workflows/build-and-deploy.yml b/Api.Localization/.github/workflows/build-and-deploy.yml index 5e3244b5..4190f5e9 100644 --- a/Api.Localization/.github/workflows/build-and-deploy.yml +++ b/Api.Localization/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Localization IMAGE_NAME: api.localization SERVICE_NAME: api-localization VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Localization/.kubernetes/configmap.yaml b/Api.Localization/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Localization/.kubernetes/configmap.yaml +++ b/Api.Localization/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Localization/.kubernetes/deployment.yaml b/Api.Localization/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Localization/.kubernetes/deployment.yaml +++ b/Api.Localization/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj b/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj +++ b/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Localization/Api.Localization/Dockerfile.Local b/Api.Localization/Api.Localization/Dockerfile.Local index 4cb9e786..9cdf2a5a 100644 --- a/Api.Localization/Api.Localization/Dockerfile.Local +++ b/Api.Localization/Api.Localization/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Localization.dll"] \ No newline at end of file diff --git a/Api.Localization/Dockerfile b/Api.Localization/Dockerfile index 14247453..8d9dd6a6 100644 --- a/Api.Localization/Dockerfile +++ b/Api.Localization/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml index 51e37842..01451c84 100644 --- a/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml +++ b/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Logging.Log4Net IMAGE_NAME: api.logging.log4net SERVICE_NAME: api-logging-log4net VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Logging.Log4Net/.kubernetes/configmap.yaml b/Api.Logging.Log4Net/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Logging.Log4Net/.kubernetes/configmap.yaml +++ b/Api.Logging.Log4Net/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Logging.Log4Net/.kubernetes/deployment.yaml b/Api.Logging.Log4Net/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Logging.Log4Net/.kubernetes/deployment.yaml +++ b/Api.Logging.Log4Net/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj b/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj index 6d16235d..efa590a5 100644 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local b/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local index 722f4503..9e7afe28 100644 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Api.Logging.Log4Net/Dockerfile b/Api.Logging.Log4Net/Dockerfile index 8ba64365..13e88f32 100644 --- a/Api.Logging.Log4Net/Dockerfile +++ b/Api.Logging.Log4Net/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml index c4343328..239840d2 100644 --- a/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml +++ b/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Logging.Microsoft IMAGE_NAME: api.logging.microsoft SERVICE_NAME: api-logging-microsoft VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Logging.Microsoft/.kubernetes/configmap.yaml b/Api.Logging.Microsoft/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Logging.Microsoft/.kubernetes/configmap.yaml +++ b/Api.Logging.Microsoft/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Logging.Microsoft/.kubernetes/deployment.yaml b/Api.Logging.Microsoft/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Logging.Microsoft/.kubernetes/deployment.yaml +++ b/Api.Logging.Microsoft/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj b/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj index a284b2dc..2bf37abf 100644 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local b/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local index adaac8c3..db46c6db 100644 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Api.Logging.Microsoft/Dockerfile b/Api.Logging.Microsoft/Dockerfile index d77a0004..efecbaea 100644 --- a/Api.Logging.Microsoft/Dockerfile +++ b/Api.Logging.Microsoft/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Logging.NLog/.github/workflows/build-and-deploy.yml b/Api.Logging.NLog/.github/workflows/build-and-deploy.yml index 16faef4c..887a4cdc 100644 --- a/Api.Logging.NLog/.github/workflows/build-and-deploy.yml +++ b/Api.Logging.NLog/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Logging.NLog IMAGE_NAME: api.logging.nlog SERVICE_NAME: api-logging-nlog VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Logging.NLog/.kubernetes/configmap.yaml b/Api.Logging.NLog/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Logging.NLog/.kubernetes/configmap.yaml +++ b/Api.Logging.NLog/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Logging.NLog/.kubernetes/deployment.yaml b/Api.Logging.NLog/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Logging.NLog/.kubernetes/deployment.yaml +++ b/Api.Logging.NLog/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj b/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj index 8989e105..6a72671e 100644 --- a/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj +++ b/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local b/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local index e47db8d4..189a4a79 100644 --- a/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local +++ b/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Logging.NLog.dll"] \ No newline at end of file diff --git a/Api.Logging.NLog/Dockerfile b/Api.Logging.NLog/Dockerfile index f7f28f9d..7a27e439 100644 --- a/Api.Logging.NLog/Dockerfile +++ b/Api.Logging.NLog/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml index c26ccdfb..bb2d2eb3 100644 --- a/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml +++ b/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Logging.Serilog IMAGE_NAME: api.logging.serilog SERVICE_NAME: api-logging-serilog VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Logging.Serilog/.kubernetes/configmap.yaml b/Api.Logging.Serilog/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Logging.Serilog/.kubernetes/configmap.yaml +++ b/Api.Logging.Serilog/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Logging.Serilog/.kubernetes/deployment.yaml b/Api.Logging.Serilog/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Logging.Serilog/.kubernetes/deployment.yaml +++ b/Api.Logging.Serilog/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj b/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj index d1fa3771..ded626a7 100644 --- a/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj +++ b/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local b/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local index 006fb412..8e77303a 100644 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Api.Logging.Serilog/Dockerfile b/Api.Logging.Serilog/Dockerfile index e211ed1c..f77d36b0 100644 --- a/Api.Logging.Serilog/Dockerfile +++ b/Api.Logging.Serilog/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.MultipartJson/.github/workflows/build-and-deploy.yml b/Api.MultipartJson/.github/workflows/build-and-deploy.yml index 8f3918cf..30954793 100644 --- a/Api.MultipartJson/.github/workflows/build-and-deploy.yml +++ b/Api.MultipartJson/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.MultipartJson IMAGE_NAME: api.multipartjson SERVICE_NAME: api-multipartjson VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.MultipartJson/.kubernetes/configmap.yaml b/Api.MultipartJson/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.MultipartJson/.kubernetes/configmap.yaml +++ b/Api.MultipartJson/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.MultipartJson/.kubernetes/deployment.yaml b/Api.MultipartJson/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.MultipartJson/.kubernetes/deployment.yaml +++ b/Api.MultipartJson/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj b/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj +++ b/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local b/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local index 8d1d3884..84d21494 100644 --- a/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local +++ b/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.MultipartJson.dll"] \ No newline at end of file diff --git a/Api.MultipartJson/Dockerfile b/Api.MultipartJson/Dockerfile index efb0b01a..17fae898 100644 --- a/Api.MultipartJson/Dockerfile +++ b/Api.MultipartJson/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml index c97c19fa..a854db52 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml @@ -2,35 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.ContentSecurityPolicy IMAGE_NAME: api.policyheaders.contentsecuritypolicy SERVICE_NAME: api-policyheaders-contentsecuritypolicy SUB_DOMAIN_NAME: nano VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,20 +36,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -137,52 +131,52 @@ jobs: } ) -join "`n" - $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + $env:GATEWAY_NAME = kubectl get gateway -n $env:KUBERNETES_NAMESPACE -o jsonpath='{.items[0].metadata.name}' Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - kubectl apply -f service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - kubectl apply -f configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - kubectl apply -f deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - kubectl apply -f autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + Get-Content .kubernetes/httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/httproute-80.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + Get-Content .kubernetes/httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/httproute-443.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -190,6 +184,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml index 65b97a51..25060955 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %SERVICE_NAME%-route + name: %SERVICE_NAME%-route-443 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml index f29775aa..e743bb46 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %APP_NAME%-route-80 + name: %SERVICE_NAME%-route-80 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local index c6f98fe9..b9640a76 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 4443 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentSecurityPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json index 23bfb871..c298b7e5 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json @@ -1,7 +1,7 @@ { "App": { "Hosting": { - "http": { + "Http": { "UseHttpsRedirection": true }, "Https": { diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile b/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile index 0bcd6f21..72e4073d 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml index 4010720a..b60f0d24 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.ContentTypeOptions IMAGE_NAME: api.policyheaders.contenttypeoptions SERVICE_NAME: api-policyheaders-contenttypeoptions VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local index c7050317..7e1bd286 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentTypeOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Dockerfile b/Api.PolicyHeaders.ContentTypeOptions/Dockerfile index fddba199..40f7676f 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/Dockerfile +++ b/Api.PolicyHeaders.ContentTypeOptions/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml index e53037ec..d1184302 100644 --- a/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.Cors IMAGE_NAME: api.policyheaders.cors SERVICE_NAME: api-policyheaders-cors VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,20 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} + CORS_ALLOWED_ORIGIN_0: web.domain.com +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +60,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +151,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +159,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml index 3977c0ee..2067fb4a 100644 --- a/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml @@ -1,8 +1,9 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config namespace: %KUBERNETES_NAMESPACE% data: App__Version: %VERSION% + App__HttpPolicyHeaders__Cors__AllowedOrigins__0: %CORS_ALLOWED_ORIGIN_0% ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local index 27de0430..97f391eb 100644 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Cors.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Dockerfile b/Api.PolicyHeaders.Cors/Dockerfile index 1c4803ef..bb8e8846 100644 --- a/Api.PolicyHeaders.Cors/Dockerfile +++ b/Api.PolicyHeaders.Cors/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml index 2d9f7da3..6c3e09af 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.ForwardedHeaders IMAGE_NAME: api.policyheaders.forwardedheaders SERVICE_NAME: api-policyheaders-forwardedheaders VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local index dcca3656..0d524c40 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ForwardedHeaders.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Dockerfile b/Api.PolicyHeaders.ForwardedHeaders/Dockerfile index 2efeaed7..93f98ec7 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/Dockerfile +++ b/Api.PolicyHeaders.ForwardedHeaders/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml index 504d5a7e..d7656d46 100644 --- a/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.FrameOptions IMAGE_NAME: api.policyheaders.frameoptions SERVICE_NAME: api-policyheaders-frameoptions VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local index bac6d4bb..f96b4e8e 100644 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.FrameOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Dockerfile b/Api.PolicyHeaders.FrameOptions/Dockerfile index dae0c7d9..b77e2d56 100644 --- a/Api.PolicyHeaders.FrameOptions/Dockerfile +++ b/Api.PolicyHeaders.FrameOptions/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml index 2a01df3e..527d003a 100644 --- a/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml @@ -2,35 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.Hsts IMAGE_NAME: api.policyheaders.hsts SERVICE_NAME: api-policyheaders-hsts SUB_DOMAIN_NAME: nano VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -38,20 +36,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -64,68 +61,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -137,52 +131,52 @@ jobs: } ) -join "`n" - $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + $env:GATEWAY_NAME = kubectl get gateway -n $env:KUBERNETES_NAMESPACE -o jsonpath='{.items[0].metadata.name}' Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - kubectl apply -f service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - kubectl apply -f configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - kubectl apply -f deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - kubectl apply -f autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + Get-Content .kubernetes/httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/httproute-80.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; - kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + Get-Content .kubernetes/httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/httproute-443.tmp.yaml; if ($LastExitCode -ne 0) { throw "error"; } - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -190,6 +184,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml index 65b97a51..25060955 100644 --- a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml +++ b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %SERVICE_NAME%-route + name: %SERVICE_NAME%-route-443 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml index f29775aa..e743bb46 100644 --- a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml +++ b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml @@ -1,7 +1,7 @@ apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: %APP_NAME%-route-80 + name: %SERVICE_NAME%-route-80 namespace: %KUBERNETES_NAMESPACE% spec: parentRefs: diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local index 86fa8383..800bceb7 100644 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 4443 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Hsts.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Dockerfile b/Api.PolicyHeaders.Hsts/Dockerfile index 477de8f6..ae132a12 100644 --- a/Api.PolicyHeaders.Hsts/Dockerfile +++ b/Api.PolicyHeaders.Hsts/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml index 9ae23278..2889f518 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.ReferrerPolicy IMAGE_NAME: api.policyheaders.referrerpolicy SERVICE_NAME: api-policyheaders-referrerpolicy VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local index fc184f16..e33b2091 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ReferrerPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Dockerfile b/Api.PolicyHeaders.ReferrerPolicy/Dockerfile index 48e82726..1337ee8f 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/Dockerfile +++ b/Api.PolicyHeaders.ReferrerPolicy/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml index 3699c699..b24e091d 100644 --- a/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.Robots IMAGE_NAME: api.policyheaders.robots SERVICE_NAME: api-policyheaders-robots VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj +++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local index 8a943b01..19cdffd3 100644 --- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local +++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Robots.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Robots/Dockerfile b/Api.PolicyHeaders.Robots/Dockerfile index f349f074..f7203acb 100644 --- a/Api.PolicyHeaders.Robots/Dockerfile +++ b/Api.PolicyHeaders.Robots/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml index 8673f1f9..af7750de 100644 --- a/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml +++ b/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.PolicyHeaders.XssProtection IMAGE_NAME: api.policyheaders.xssprotection SERVICE_NAME: api-policyheaders-xssprotection VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml +++ b/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml +++ b/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj index 890be2f1..9b70eeab 100644 --- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj +++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local index 0564fe65..59bc4b13 100644 --- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local +++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.PolicyHeaders.XssProtection.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/Dockerfile b/Api.PolicyHeaders.XssProtection/Dockerfile index b9e1371d..605a1811 100644 --- a/Api.PolicyHeaders.XssProtection/Dockerfile +++ b/Api.PolicyHeaders.XssProtection/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.RequestTracing/.github/workflows/build-and-deploy.yml b/Api.RequestTracing/.github/workflows/build-and-deploy.yml index 0f92a439..ef29ed2f 100644 --- a/Api.RequestTracing/.github/workflows/build-and-deploy.yml +++ b/Api.RequestTracing/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.RequestTracing IMAGE_NAME: api.requesttracing SERVICE_NAME: api-requesttracing VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.RequestTracing/.kubernetes/configmap.yaml b/Api.RequestTracing/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.RequestTracing/.kubernetes/configmap.yaml +++ b/Api.RequestTracing/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.RequestTracing/.kubernetes/deployment.yaml b/Api.RequestTracing/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.RequestTracing/.kubernetes/deployment.yaml +++ b/Api.RequestTracing/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj b/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj +++ b/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local b/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local index 87cc5040..85a3edd9 100644 --- a/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local +++ b/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.RequestTracing.dll"] \ No newline at end of file diff --git a/Api.RequestTracing/Dockerfile b/Api.RequestTracing/Dockerfile index 316aafa3..30be91b7 100644 --- a/Api.RequestTracing/Dockerfile +++ b/Api.RequestTracing/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ResponseCache/.github/workflows/build-and-deploy.yml b/Api.ResponseCache/.github/workflows/build-and-deploy.yml index b01ba103..7b0df2ac 100644 --- a/Api.ResponseCache/.github/workflows/build-and-deploy.yml +++ b/Api.ResponseCache/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ResponseCache IMAGE_NAME: api.responsecache SERVICE_NAME: api-responsecache VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ResponseCache/.kubernetes/configmap.yaml b/Api.ResponseCache/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ResponseCache/.kubernetes/configmap.yaml +++ b/Api.ResponseCache/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ResponseCache/.kubernetes/deployment.yaml b/Api.ResponseCache/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.ResponseCache/.kubernetes/deployment.yaml +++ b/Api.ResponseCache/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj b/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj +++ b/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local b/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local index 7062f25b..ef77c5cf 100644 --- a/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local +++ b/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ResponseCache.dll"] \ No newline at end of file diff --git a/Api.ResponseCache/Dockerfile b/Api.ResponseCache/Dockerfile index 3dbc645f..66a2a6df 100644 --- a/Api.ResponseCache/Dockerfile +++ b/Api.ResponseCache/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.ResponseCompression/.github/workflows/build-and-deploy.yml b/Api.ResponseCompression/.github/workflows/build-and-deploy.yml index 5006980a..b4538268 100644 --- a/Api.ResponseCompression/.github/workflows/build-and-deploy.yml +++ b/Api.ResponseCompression/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.ResponseCompression IMAGE_NAME: api.responsecompression SERVICE_NAME: api-responsecompression VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.ResponseCompression/.kubernetes/configmap.yaml b/Api.ResponseCompression/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.ResponseCompression/.kubernetes/configmap.yaml +++ b/Api.ResponseCompression/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.ResponseCompression/.kubernetes/deployment.yaml b/Api.ResponseCompression/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.ResponseCompression/.kubernetes/deployment.yaml +++ b/Api.ResponseCompression/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj b/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj +++ b/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local b/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local index 53c17eb8..02cb186b 100644 --- a/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local +++ b/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.ResponseCompression.dll"] \ No newline at end of file diff --git a/Api.ResponseCompression/Dockerfile b/Api.ResponseCompression/Dockerfile index e0f4803d..32666ffc 100644 --- a/Api.ResponseCompression/Dockerfile +++ b/Api.ResponseCompression/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Session/.github/workflows/build-and-deploy.yml b/Api.Session/.github/workflows/build-and-deploy.yml index 37ef11b0..3102c3e1 100644 --- a/Api.Session/.github/workflows/build-and-deploy.yml +++ b/Api.Session/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Session IMAGE_NAME: api.session SERVICE_NAME: api-session VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Session/.kubernetes/configmap.yaml b/Api.Session/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Session/.kubernetes/configmap.yaml +++ b/Api.Session/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Session/.kubernetes/deployment.yaml b/Api.Session/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.Session/.kubernetes/deployment.yaml +++ b/Api.Session/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.Session/Api.Session.Models/Api.Session.Models.csproj b/Api.Session/Api.Session.Models/Api.Session.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Session/Api.Session.Models/Api.Session.Models.csproj +++ b/Api.Session/Api.Session.Models/Api.Session.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Session/Api.Session/Dockerfile.Local b/Api.Session/Api.Session/Dockerfile.Local index fb4b06aa..93481dcb 100644 --- a/Api.Session/Api.Session/Dockerfile.Local +++ b/Api.Session/Api.Session/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Session.dll"] \ No newline at end of file diff --git a/Api.Session/Dockerfile b/Api.Session/Dockerfile index 7b0f7c94..cc1fda5c 100644 --- a/Api.Session/Dockerfile +++ b/Api.Session/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.StartupTasks/.github/workflows/build-and-deploy.yml b/Api.StartupTasks/.github/workflows/build-and-deploy.yml index 07fe4bdc..138cb1ab 100644 --- a/Api.StartupTasks/.github/workflows/build-and-deploy.yml +++ b/Api.StartupTasks/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.StartupTasks IMAGE_NAME: api.startuptasks SERVICE_NAME: api-startuptasks VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.StartupTasks/.kubernetes/configmap.yaml b/Api.StartupTasks/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.StartupTasks/.kubernetes/configmap.yaml +++ b/Api.StartupTasks/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.StartupTasks/.kubernetes/deployment.yaml b/Api.StartupTasks/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.StartupTasks/.kubernetes/deployment.yaml +++ b/Api.StartupTasks/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj b/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj +++ b/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local b/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local index 1a5748f0..cd848c63 100644 --- a/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local +++ b/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.StartupTasks.dll"] \ No newline at end of file diff --git a/Api.StartupTasks/Dockerfile b/Api.StartupTasks/Dockerfile index 13ab1f4e..3551b58d 100644 --- a/Api.StartupTasks/Dockerfile +++ b/Api.StartupTasks/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.StaticFiles/.github/workflows/build-and-deploy.yml b/Api.StaticFiles/.github/workflows/build-and-deploy.yml index cdc4dcb8..1895d321 100644 --- a/Api.StaticFiles/.github/workflows/build-and-deploy.yml +++ b/Api.StaticFiles/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.StaticFiles IMAGE_NAME: api.staticfiles SERVICE_NAME: api-staticfiles VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.StaticFiles/.kubernetes/configmap.yaml b/Api.StaticFiles/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.StaticFiles/.kubernetes/configmap.yaml +++ b/Api.StaticFiles/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.StaticFiles/.kubernetes/deployment.yaml b/Api.StaticFiles/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.StaticFiles/.kubernetes/deployment.yaml +++ b/Api.StaticFiles/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj b/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj +++ b/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local b/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local index ad327791..93bccd3d 100644 --- a/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local +++ b/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.StaticFiles.dll"] \ No newline at end of file diff --git a/Api.StaticFiles/Dockerfile b/Api.StaticFiles/Dockerfile index ab17eafa..1c0bbf52 100644 --- a/Api.StaticFiles/Dockerfile +++ b/Api.StaticFiles/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Storage.Azure/.github/workflows/build-and-deploy.yml b/Api.Storage.Azure/.github/workflows/build-and-deploy.yml index 2b882124..47920047 100644 --- a/Api.Storage.Azure/.github/workflows/build-and-deploy.yml +++ b/Api.Storage.Azure/.github/workflows/build-and-deploy.yml @@ -2,35 +2,33 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Storage.Azure IMAGE_NAME: api.storage.azure SERVICE_NAME: api-storage-azure VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} - AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_RESOURCE_GROUP_STORAGE }} + AZURE_GROUP_BACKUP: ${{ vars.AZURE_RESOURCE_GROUP_BACKUP }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -40,20 +38,16 @@ env: KUBERNETES_CPU_SCALING: 180 STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-azure - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -66,26 +60,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Build & Push Image + shell: pwsh + run: | + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Create Fileshare shell: pwsh run: | @@ -105,72 +138,97 @@ jobs: --storage-account $env:STORAGE_ACCOUNT_NAME ` --access-tier TransactionOptimized ` --quota $env:STORAGE_SIZE; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; - - az backup protection enable-for-azurefileshare ` - -g $env:AZURE_GROUP_BACKUP ` - -v $env:BACKUP_VAULT_NAME ` - -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + } + else + { + az storage share-rm update ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` - --azure-file-share $env:STORAGE_SHARE_NAME; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; } - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - if ($LastExitCode -ne 0) { throw "error"; }; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-fileshare-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + if ($LastExitCode -ne 0) { throw "error"; - }; + }; - - name: Publish NuGet + - name: Managed Identity & Federated Credentials shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - if ($LastExitCode -ne 0) - { + $env:IDENTITY_NAME = $env:SERVICE_NAME + "-identity"; + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + $env:KUBERNETES_ISSUER_URL = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].['oidcIssuerProfile.issuerUrl'] -o tsv; + $env:STORAGE_ACCOUNT_ID = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].id -o tsv; + + if (-not $env:IDENTITY_PRINCIPAL_ID) + { + az identity create ` + -g $env:AZURE_GROUP_KUBERNETES ` + -n $env:IDENTITY_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + } + + az role assignment create ` + --assignee-object-id $env:IDENTITY_PRINCIPAL_ID ` + --assignee-principal-type ServicePrincipal ` + --role "Storage File Data SMB MI Admin" ` + --scope $env:STORAGE_ACCOUNT_ID + + if ($LastExitCode -ne 0) + { throw "error"; - }; + }; + + az identity federated-credential create ` + --name $env:SERVICE_NAME-credentials ` + --resource-group $env:AZURE_GROUP_KUBERNETES ` + --identity-name $env:IDENTITY_NAME ` + --issuer $env:KUBERNETES_ISSUER_URL ` + --subject "system:serviceaccount:${env:KUBERNETES_NAMESPACE}:${env:SERVICE_NAME}-service-account" ` + --audience api://AzureADTokenExchange; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "IDENTITY_NAME=$env:IDENTITY_NAME" >> $env:GITHUB_ENV; - name: Kubernetes Deploy shell: pwsh run: | + $env:IDENTITY_CLIENT_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query clientId -o tsv; + $env:VOLUME_NAME_SUFFIX = $env:IDENTITY_CLIENT_ID.Substring(0, 5); + + Get-Content .kubernetes/service-account.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service-account.tmp.yaml; + kubectl apply -f .kubernetes/service-account.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + Get-Content .kubernetes/storage-pv.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pv.tmp.yaml; kubectl apply -f .kubernetes/storage-pv.tmp.yaml; if ($LastExitCode -ne 0) @@ -214,7 +272,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -222,6 +280,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Storage.Azure/.kubernetes/configmap.yaml b/Api.Storage.Azure/.kubernetes/configmap.yaml index b64a515d..ef1e83f5 100644 --- a/Api.Storage.Azure/.kubernetes/configmap.yaml +++ b/Api.Storage.Azure/.kubernetes/configmap.yaml @@ -1,9 +1,9 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config namespace: %KUBERNETES_NAMESPACE% data: App__Version: %VERSION% - Storage__HealthCheck__AccountName: %STORAGE_ACCOUNT_NAME% + Storage__ShareName: %STORAGE_SHARE_NAME% ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Azure/.kubernetes/deployment.yaml b/Api.Storage.Azure/.kubernetes/deployment.yaml index 9eb44e22..f2e7ad15 100644 --- a/Api.Storage.Azure/.kubernetes/deployment.yaml +++ b/Api.Storage.Azure/.kubernetes/deployment.yaml @@ -15,8 +15,9 @@ spec: metadata: labels: app: %SERVICE_NAME% + azure.workload.identity/use: "true" spec: - automountServiceAccountToken: false + serviceAccountName: %SERVICE_NAME%-service-account securityContext: runAsUser: 1000 runAsGroup: 2000 @@ -86,9 +87,7 @@ spec: volumes: - name: %SERVICE_NAME%-volume persistentVolumeClaim: - claimName: %SERVICE_NAME%-azurefile-pvc + claimName: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% - name: tmp emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.Storage.Azure/.kubernetes/service-account.yaml b/Api.Storage.Azure/.kubernetes/service-account.yaml new file mode 100644 index 00000000..283750fd --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/service-account.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: %SERVICE_NAME%-service-account + namespace: %KUBERNETES_NAMESPACE% + annotations: + azure.workload.identity/client-id: %IDENTITY_CLIENT_ID% \ No newline at end of file diff --git a/Api.Storage.Azure/.kubernetes/storage-pv.yaml b/Api.Storage.Azure/.kubernetes/storage-pv.yaml index b6281fb8..5bc60f12 100644 --- a/Api.Storage.Azure/.kubernetes/storage-pv.yaml +++ b/Api.Storage.Azure/.kubernetes/storage-pv.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: %SERVICE_NAME%-azurefile-pv + name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% spec: capacity: storage: %STORAGE_SIZE% @@ -9,9 +9,20 @@ spec: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-static + mountOptions: + - dir_mode=0777 + - file_mode=0777 + - uid=0 + - gid=0 + claimRef: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% csi: driver: file.csi.azure.com - volumeHandle: %STORAGE_SHARE_NAME% + volumeHandle: %AZURE_GROUP_STORAGE%#%STORAGE_ACCOUNT_NAME%#%STORAGE_SHARE_NAME%-%VOLUME_NAME_SUFFIX% volumeAttributes: shareName: %STORAGE_SHARE_NAME% - storageAccount: %STORAGE_ACCOUNT_NAME% \ No newline at end of file + storageAccount: %STORAGE_ACCOUNT_NAME% + resourceGroup: %AZURE_GROUP_STORAGE% + clientID: %IDENTITY_CLIENT_ID% + mountWithWorkloadIdentityToken: "true" \ No newline at end of file diff --git a/Api.Storage.Azure/.kubernetes/storage-pvc.yaml b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml index 252c7857..8ce63544 100644 --- a/Api.Storage.Azure/.kubernetes/storage-pvc.yaml +++ b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: %SERVICE_NAME%-azurefile-pvc + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% namespace: %KUBERNETES_NAMESPACE% spec: accessModes: @@ -9,4 +9,5 @@ spec: storageClassName: azurefile-static resources: requests: - storage: %STORAGE_SIZE% \ No newline at end of file + storage: %STORAGE_SIZE% + volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj b/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj index 2865dfbb..df88cb1c 100644 --- a/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj +++ b/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Storage.Azure/Api.Storage.Azure.sln b/Api.Storage.Azure/Api.Storage.Azure.sln index a75a6568..11c3e764 100644 --- a/Api.Storage.Azure/Api.Storage.Azure.sln +++ b/Api.Storage.Azure/Api.Storage.Azure.sln @@ -22,6 +22,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes" .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml .kubernetes\configmap.yaml = .kubernetes\configmap.yaml .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service-account.yaml = .kubernetes\service-account.yaml .kubernetes\service.yaml = .kubernetes\service.yaml .kubernetes\storage-pv.yaml = .kubernetes\storage-pv.yaml .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml diff --git a/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local b/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local index eeb40cec..772f5036 100644 --- a/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local +++ b/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Storage.Azure.dll"] \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.json index 645e4243..2c668037 100644 --- a/Api.Storage.Azure/Api.Storage.Azure/appsettings.json +++ b/Api.Storage.Azure/Api.Storage.Azure/appsettings.json @@ -15,8 +15,7 @@ "Storage": { "ShareName": "nano-storage-azure", "HealthCheck": { - "AccountName": null, - "UnhealthyStatus": "Degraded" + "UnhealthyStatus": "Unhealthy" } } } \ No newline at end of file diff --git a/Api.Storage.Azure/Dockerfile b/Api.Storage.Azure/Dockerfile index 6b462057..606c0e91 100644 --- a/Api.Storage.Azure/Dockerfile +++ b/Api.Storage.Azure/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Storage.Azure/README.md b/Api.Storage.Azure/README.md index b4382605..d1489d0a 100644 --- a/Api.Storage.Azure/README.md +++ b/Api.Storage.Azure/README.md @@ -25,11 +25,13 @@ This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lesso that inherits from the top-level Nano `BaseController`. This application demonstrates uploading a file and saving it to a mapped file share. + When running locally, files are **NOT** written to the Azure File Share. Instead, Docker mounts a local directory to simulate the file share. + Files are saved in `.docker/bin/`. -A storage health check is configured to target the Azure File Share, but it requires valid credentials to be provided under `Storage.Credentials`. If the -credentials are omitted from the configuration, the application will still run, but the health-check will report `degraded`. +A storage health check is configured to target the Azure File Share, but it requires valid credentials to be provided under `Storage.Credentials`. If the credentials are omitted +from the configuration, the application will still run, but the health-check will report `degraded`. Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check JSON response. @@ -62,8 +64,7 @@ Add the storage configuration. "Storage": { "ShareName": "nano-storage-azure", "HealthCheck": { - "AccountName": null, - "UnhealthyStatus": "Degraded" + "UnhealthyStatus": "Unhealthy" } } ``` @@ -81,18 +82,79 @@ Additionally, application health-checks have been enabled with the configuration Mapped the fileshare in `docker-compose.yml`. ```yaml -docker +services: + svc.accounts: volumes: - ./bin/nano-storage-azure:/mnt/nano-storage-azure ``` ## Kubernetes -Added two new kubernetes templaets, the `storage-pv.yaml` and `storage-pvc.yaml`. Updated the `deployment.yaml` mounting the volume. +Added three new kubernetes templaets, the `storage-pv.yaml`, `storage-pvc.yaml`, and the `service-account.yaml`. + +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% +spec: + capacity: + storage: %STORAGE_SIZE% + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: azurefile-static + mountOptions: + - dir_mode=0777 + - file_mode=0777 + - uid=0 + - gid=0 + claimRef: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% + csi: + driver: file.csi.azure.com + volumeHandle: %AZURE_GROUP_STORAGE%#%STORAGE_ACCOUNT_NAME%#%STORAGE_SHARE_NAME%-%VOLUME_NAME_SUFFIX% + volumeAttributes: + shareName: %STORAGE_SHARE_NAME% + storageAccount: %STORAGE_ACCOUNT_NAME% + resourceGroup: %AZURE_GROUP_STORAGE% + clientID: %IDENTITY_CLIENT_ID% + mountWithWorkloadIdentityToken: "true" +``` + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% +spec: + accessModes: + - ReadWriteMany + storageClassName: azurefile-static + resources: + requests: + storage: %STORAGE_SIZE% + volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% +``` + +Also, the `configmap.yaml` should have the share-name included. + +``` +data: + Storage__ShareName: %STORAGE_SHARE_NAME% +``` + +Updated the `deployment.yaml` mounting the volume. ```yaml spec: template: + metadata: + labels: + azure.workload.identity/use: "true" spec: + serviceAccountName: %SERVICE_NAME%-service-account containers: volumeMounts: - name: %SERVICE_NAME%-volume @@ -102,33 +164,34 @@ spec: volumes: - name: %SERVICE_NAME%-volume persistentVolumeClaim: - claimName: %SERVICE_NAME%-azurefile-pvc + claimName: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% - name: tmp emptyDir: {} ``` -Additionally, the `configmap.yaml` file stores the `$env:STORAGE_ACCOUNT_NAME` value, which is used by the TCP-based health check to validate connectivity to the Azure File Share endpoint. +Notice the name suffix for `claimName`. The PersistentVolume (PV) and PersistentVolumeClaim (PVC) are created with a suffix to allow the managed identity used for authenticating with the +Azure File Share to be changed without trying to replace existing immutable resources. ## GitHub Actions Add the following environment variables to the `buid-and-deply.yml`. ```yaml -env: - AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} - AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} +env: + AZURE_GROUP_BACKUP: ${{ vars.AZURE_RESOURCE_GROUP_BACKUP }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_RESOURCE_GROUP_STORAGE }} STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-azure ``` -And add this step below as well, ensuring that the fileshare gets created before the application is deployed. +Additionally, this step has been added to ensure the file share is created before the application is deployed. ```yaml - name: Create Fileshare shell: pwsh run: | - $env:STORAGE_ACCOUNT_NAME = sudo az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + $env:STORAGE_ACCOUNT_NAME = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; - $env:FILE_SHARE_EXISTS = sudo az storage share-rm exists ` + $env:FILE_SHARE_EXISTS = az storage share-rm exists ` -g $env:AZURE_GROUP_STORAGE ` -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` @@ -136,30 +199,100 @@ And add this step below as well, ensuring that the fileshare gets created before if ($env:FILE_SHARE_EXISTS -eq "false") { - sudo az storage share-rm create ` + az storage share-rm create ` -g $env:AZURE_GROUP_STORAGE ` -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` --access-tier TransactionOptimized ` --quota $env:STORAGE_SIZE; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + } + else + { + az storage share-rm update ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + } + + if ($LastExitCode -ne 0) + { + throw "error"; + }; - $env:BACKUP_VAULT_NAME = sudo az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; - sudo az backup protection enable-for-azurefileshare ` - -g $env:AZURE_GROUP_BACKUP ` - -v $env:BACKUP_VAULT_NAME ` - -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` - --storage-account $env:STORAGE_ACCOUNT_NAME ` - --azure-file-share $env:STORAGE_SHARE_NAME; - - if ($LastExitCode -ne 0) - { + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-fileshare-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; +``` + +...and includes a step to create a managed identity and federated credentials for authenticating with the storage account. + +``` +- name: Managed Identity & Federated Credentials + shell: pwsh + run: | + $env:IDENTITY_NAME = $env:SERVICE_NAME + "-identity"; + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + $env:KUBERNETES_ISSUER_URL = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].['oidcIssuerProfile.issuerUrl'] -o tsv; + $env:STORAGE_ACCOUNT_ID = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].id -o tsv; + + if (-not $env:IDENTITY_PRINCIPAL_ID) + { + az identity create ` + -g $env:AZURE_GROUP_KUBERNETES ` + -n $env:IDENTITY_NAME; + + if ($LastExitCode -ne 0) + { throw "error"; }; + + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; } -``` \ No newline at end of file + + az role assignment create ` + --assignee-object-id $env:IDENTITY_PRINCIPAL_ID ` + --assignee-principal-type ServicePrincipal ` + --role "Storage File Data SMB MI Admin" ` + --scope $env:STORAGE_ACCOUNT_ID + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + az identity federated-credential create ` + --name $env:SERVICE_NAME-credentials ` + --resource-group $env:AZURE_GROUP_KUBERNETES ` + --identity-name $env:IDENTITY_NAME ` + --issuer $env:KUBERNETES_ISSUER_URL ` + --subject "system:serviceaccount:${env:KUBERNETES_NAMESPACE}:${env:SERVICE_NAME}-service-account" ` + --audience api://AzureADTokenExchange; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "IDENTITY_NAME=$env:IDENTITY_NAME" >> $env:GITHUB_ENV; +``` + +Last, during the Kubernetes deployment step, before any resources are applied, environmental variables required for the new `stoerage-pv.yaml` and `stoerage-pvc.yaml` must be set. + +```powershell +$env:IDENTITY_CLIENT_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query clientId -o tsv; +$env:VOLUME_NAME_SUFFIX = $env:IDENTITY_CLIENT_ID.Substring(0, 5); +``` + +The deployment commands have been updated to apply the new Kubernetes storage templates. diff --git a/Api.Storage.Local/.github/workflows/build-and-deploy.yml b/Api.Storage.Local/.github/workflows/build-and-deploy.yml index 3044ba11..13b0c3d9 100644 --- a/Api.Storage.Local/.github/workflows/build-and-deploy.yml +++ b/Api.Storage.Local/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Storage.Local IMAGE_NAME: api.storage.local SERVICE_NAME: api-storage-local VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -37,20 +35,19 @@ env: KUBERNETES_CPU_LIMIT: 600m STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-local - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -63,68 +60,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -171,7 +165,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -179,6 +173,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Storage.Local/.kubernetes/configmap.yaml b/Api.Storage.Local/.kubernetes/configmap.yaml index 3977c0ee..ef1e83f5 100644 --- a/Api.Storage.Local/.kubernetes/configmap.yaml +++ b/Api.Storage.Local/.kubernetes/configmap.yaml @@ -1,8 +1,9 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config namespace: %KUBERNETES_NAMESPACE% data: App__Version: %VERSION% + Storage__ShareName: %STORAGE_SHARE_NAME% ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Local/.kubernetes/deployment.yaml b/Api.Storage.Local/.kubernetes/deployment.yaml index eb1c9093..0fcc7ffb 100644 --- a/Api.Storage.Local/.kubernetes/deployment.yaml +++ b/Api.Storage.Local/.kubernetes/deployment.yaml @@ -84,6 +84,3 @@ spec: claimName: %SERVICE_NAME%-pvc - name: tmp emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Storage.Local/.kubernetes/storage-storageclass.yaml b/Api.Storage.Local/.kubernetes/storage-storageclass.yaml index f9ccaa3e..fa2c6dd3 100644 --- a/Api.Storage.Local/.kubernetes/storage-storageclass.yaml +++ b/Api.Storage.Local/.kubernetes/storage-storageclass.yaml @@ -2,7 +2,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: %SERVICE_NAME%-storage-class -provisioner: kubernetes.io/azure-disk +provisioner: disk.csi.azure.com parameters: storageaccounttype: Standard_LRS kind: Managed diff --git a/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj b/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj index 8f47aa23..08ff690d 100644 --- a/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj +++ b/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local b/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local index 6512ea12..2f0019a3 100644 --- a/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local +++ b/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Storage.Local.dll"] \ No newline at end of file diff --git a/Api.Storage.Local/Dockerfile b/Api.Storage.Local/Dockerfile index 657921b0..ceeec3ed 100644 --- a/Api.Storage.Local/Dockerfile +++ b/Api.Storage.Local/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Storage.Local/README.md b/Api.Storage.Local/README.md index c243734f..6156afb4 100644 --- a/Api.Storage.Local/README.md +++ b/Api.Storage.Local/README.md @@ -57,7 +57,7 @@ Configured the application with the necessary storage setup. "Storage": { "ShareName": "nano-storage-local", "HealthCheck": { - "UnhealthyStatus": "Degraded" + "UnhealthyStatus": "Unhealthy" } } ``` @@ -75,7 +75,8 @@ Additionally, application health-checks have been enabled with the configuration Mapped the fileshare in `docker-compose.yml`. ```yaml -docker +services: + console.storage.local: volumes: - ./bin/nano-storage-local:/mnt/nano-storage-local ``` @@ -113,11 +114,3 @@ env: ``` Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Api.TimeZone/.github/workflows/build-and-deploy.yml b/Api.TimeZone/.github/workflows/build-and-deploy.yml index edbde0d8..b1a29486 100644 --- a/Api.TimeZone/.github/workflows/build-and-deploy.yml +++ b/Api.TimeZone/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.TimeZone IMAGE_NAME: api.timezone SERVICE_NAME: api-timezone VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.TimeZone/.kubernetes/configmap.yaml b/Api.TimeZone/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.TimeZone/.kubernetes/configmap.yaml +++ b/Api.TimeZone/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.TimeZone/.kubernetes/deployment.yaml b/Api.TimeZone/.kubernetes/deployment.yaml index 02882c56..00eb67cf 100644 --- a/Api.TimeZone/.kubernetes/deployment.yaml +++ b/Api.TimeZone/.kubernetes/deployment.yaml @@ -73,6 +73,4 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj b/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj +++ b/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.TimeZone/Api.TimeZone/Dockerfile.Local b/Api.TimeZone/Api.TimeZone/Dockerfile.Local index 0ab8570b..b64cb62b 100644 --- a/Api.TimeZone/Api.TimeZone/Dockerfile.Local +++ b/Api.TimeZone/Api.TimeZone/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.TimeZone.dll"] \ No newline at end of file diff --git a/Api.TimeZone/Dockerfile b/Api.TimeZone/Dockerfile index f7c7188e..31423bbd 100644 --- a/Api.TimeZone/Dockerfile +++ b/Api.TimeZone/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.Versioning/.github/workflows/build-and-deploy.yml b/Api.Versioning/.github/workflows/build-and-deploy.yml index 3a01b8e4..15a4e398 100644 --- a/Api.Versioning/.github/workflows/build-and-deploy.yml +++ b/Api.Versioning/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Versioning IMAGE_NAME: api.versioning SERVICE_NAME: api-versioning VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.Versioning/.kubernetes/configmap.yaml b/Api.Versioning/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.Versioning/.kubernetes/configmap.yaml +++ b/Api.Versioning/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.Versioning/.kubernetes/deployment.yaml b/Api.Versioning/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.Versioning/.kubernetes/deployment.yaml +++ b/Api.Versioning/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj b/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj +++ b/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.Versioning/Api.Versioning/Dockerfile.Local b/Api.Versioning/Api.Versioning/Dockerfile.Local index 273713a4..41c3c823 100644 --- a/Api.Versioning/Api.Versioning/Dockerfile.Local +++ b/Api.Versioning/Api.Versioning/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Versioning.dll"] \ No newline at end of file diff --git a/Api.Versioning/Dockerfile b/Api.Versioning/Dockerfile index 079b95c9..aa2c3f66 100644 --- a/Api.Versioning/Dockerfile +++ b/Api.Versioning/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api.VirusScan/.github/workflows/build-and-deploy.yml b/Api.VirusScan/.github/workflows/build-and-deploy.yml index 0e0eb79a..f7338576 100644 --- a/Api.VirusScan/.github/workflows/build-and-deploy.yml +++ b/Api.VirusScan/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.VirusScan IMAGE_NAME: api.virusscan SERVICE_NAME: api-virusscan VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +59,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api.VirusScan/.kubernetes/configmap.yaml b/Api.VirusScan/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api.VirusScan/.kubernetes/configmap.yaml +++ b/Api.VirusScan/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api.VirusScan/.kubernetes/deployment.yaml b/Api.VirusScan/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Api.VirusScan/.kubernetes/deployment.yaml +++ b/Api.VirusScan/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj b/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj +++ b/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api.VirusScan/Api.VirusScan/Dockerfile.Local b/Api.VirusScan/Api.VirusScan/Dockerfile.Local index bcc73a22..a9fc193d 100644 --- a/Api.VirusScan/Api.VirusScan/Dockerfile.Local +++ b/Api.VirusScan/Api.VirusScan/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.VirusScan.dll"] \ No newline at end of file diff --git a/Api.VirusScan/Dockerfile b/Api.VirusScan/Dockerfile index 6fde6c7c..7ff85e3b 100644 --- a/Api.VirusScan/Dockerfile +++ b/Api.VirusScan/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Api._Blank/.github/workflows/build-and-deploy.yml b/Api._Blank/.github/workflows/build-and-deploy.yml index 11902a3d..2ee847e3 100644 --- a/Api._Blank/.github/workflows/build-and-deploy.yml +++ b/Api._Blank/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Api.Blank IMAGE_NAME: api.blank SERVICE_NAME: api-blank VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,18 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,68 +58,65 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; if ($LastExitCode -ne 0) { throw "error"; }; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -156,7 +149,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +157,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Api._Blank/.kubernetes/configmap.yaml b/Api._Blank/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Api._Blank/.kubernetes/configmap.yaml +++ b/Api._Blank/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Api._Blank/.kubernetes/deployment.yaml b/Api._Blank/.kubernetes/deployment.yaml index b15e7e29..085e7961 100644 --- a/Api._Blank/.kubernetes/deployment.yaml +++ b/Api._Blank/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj b/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj index c33a8e9a..e4188943 100644 --- a/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj +++ b/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj @@ -22,17 +22,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -41,7 +35,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Api._Blank/Api.Blank/Dockerfile.Local b/Api._Blank/Api.Blank/Dockerfile.Local index 9c84943b..a43abac6 100644 --- a/Api._Blank/Api.Blank/Dockerfile.Local +++ b/Api._Blank/Api.Blank/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Api.Blank.dll"] \ No newline at end of file diff --git a/Api._Blank/Dockerfile b/Api._Blank/Dockerfile index c06b6792..ce52c1c9 100644 --- a/Api._Blank/Dockerfile +++ b/Api._Blank/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml index 930c99ac..96bdd122 100644 --- a/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml +++ b/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.CustomConfigSection IMAGE_NAME: console.customconfigsection SERVICE_NAME: console-customconfigsection VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.CustomConfigSection/.kubernetes/cronjob.yaml b/Console.CustomConfigSection/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.CustomConfigSection/.kubernetes/cronjob.yaml +++ b/Console.CustomConfigSection/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.CustomService/.github/workflows/build-and-deploy.yml b/Console.CustomService/.github/workflows/build-and-deploy.yml index 784ed671..2fbe6222 100644 --- a/Console.CustomService/.github/workflows/build-and-deploy.yml +++ b/Console.CustomService/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.CustomService IMAGE_NAME: console.customservice SERVICE_NAME: console-customservice VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.CustomService/.kubernetes/cronjob.yaml b/Console.CustomService/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.CustomService/.kubernetes/cronjob.yaml +++ b/Console.CustomService/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml index 9733b697..62befd94 100644 --- a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml +++ b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml @@ -2,51 +2,49 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Data.InMemory IMAGE_NAME: console.data.inmemory SERVICE_NAME: console-data-inmemory VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +57,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +122,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +130,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Data.InMemory/.kubernetes/cronjob.yaml b/Console.Data.InMemory/.kubernetes/cronjob.yaml index 71ff564c..e10e9bd5 100644 --- a/Console.Data.InMemory/.kubernetes/cronjob.yaml +++ b/Console.Data.InMemory/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json index 254689c8..e0008651 100644 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json @@ -17,7 +17,7 @@ "DefaultCollation": null, "ConnectionString": "nanoDb", "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, diff --git a/Console.Data.InMemory/README.md b/Console.Data.InMemory/README.md index d6cb69d4..5515d9ac 100644 --- a/Console.Data.InMemory/README.md +++ b/Console.Data.InMemory/README.md @@ -61,7 +61,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": "nanoDb", "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, diff --git a/Console.Data.MySql/.github/workflows/build-and-deploy.yml b/Console.Data.MySql/.github/workflows/build-and-deploy.yml index 7e45a90a..98282ad2 100644 --- a/Console.Data.MySql/.github/workflows/build-and-deploy.yml +++ b/Console.Data.MySql/.github/workflows/build-and-deploy.yml @@ -2,31 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Data.MySql IMAGE_NAME: console.data.mysql SERVICE_NAME: console-data-mysql VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi @@ -35,22 +35,21 @@ env: KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -63,75 +62,76 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD + $userExists = mysql ` --host $env:SQL_HOST ` --port $env:SQL_PORT ` @@ -160,7 +160,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; Get-Content .kubernetes/sql-auth-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/sql-auth-secret.tmp.yaml; kubectl apply -f .kubernetes/sql-auth-secret.tmp.yaml; @@ -184,7 +184,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -192,6 +192,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml b/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml +++ b/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Console.Data.MySql/.kubernetes/cronjob.yaml b/Console.Data.MySql/.kubernetes/cronjob.yaml index 9ae388c3..a5d8b30b 100644 --- a/Console.Data.MySql/.kubernetes/cronjob.yaml +++ b/Console.Data.MySql/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -53,5 +54,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json index 34379ce9..670c9b68 100644 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json @@ -1,6 +1,6 @@ { "Data": { "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=root;Pwd=myPassword_123" } } \ No newline at end of file diff --git a/Console.Data.MySql/README.md b/Console.Data.MySql/README.md index 7d5948c4..d19e08da 100644 --- a/Console.Data.MySql/README.md +++ b/Console.Data.MySql/README.md @@ -107,7 +107,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `cronjob.yaml`. ```json spec: @@ -129,6 +129,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -141,34 +143,56 @@ Additionally, this step has been added to ensure database migrations are applied - name: Database Migration & User shell: pwsh run: | - $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName; - $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort; - $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Required"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - apt-get update; - apt-get install -y mysql-client; + $env:MYSQL_PWD = $env:SQL_ADMIN_PASSWORD - $userExists = mysql --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');" $env:SQL_MIGRATION_CONNECTIONSTRING; + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; if ($userExists -eq 0) { - mysql --connect-expired-password -e " ` - CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:SQL_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:SQL_MIGRATION_CONNECTIONSTRING; + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; ``` -Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. +Last, before applying the new Kubernetes templates, these environmental variables must be set. + +```powershell +$env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Required"; +``` +Finally, apply the templates. diff --git a/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml index 8e9abbbf..fa0d8692 100644 --- a/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml +++ b/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml @@ -2,55 +2,54 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Data.PostgreSQL IMAGE_NAME: console.data.postgresql SERVICE_NAME: console-data-postgresql VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-mysql-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -63,77 +62,74 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + ./ - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Database Migration & User shell: pwsh run: | $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "5432"; $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; + + $env:DATA__CONNECTIONSTRING = $env:SQL_MIGRATION_CONNECTIONSTRING; - dotnet ef database update ` + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - - apt-get update - apt-get install -y postgresql-client $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" @@ -168,7 +164,7 @@ jobs: - name: Kubernetes Deploy shell: pwsh run: | - $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; @@ -192,7 +188,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -200,6 +196,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml b/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml +++ b/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml b/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml index 9ae388c3..a5d8b30b 100644 --- a/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml +++ b/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -53,5 +54,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.PostgreSQL/README.md b/Console.Data.PostgreSQL/README.md index d27443e3..963e46ba 100644 --- a/Console.Data.PostgreSQL/README.md +++ b/Console.Data.PostgreSQL/README.md @@ -105,7 +105,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `cronjob.yaml`. ```json spec: @@ -125,6 +125,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-postgres-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -142,18 +144,19 @@ Additionally, this step has been added to ensure database migrations are applied $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + $env:DATA__CONNECTIONSTRING = $env:SQL_MIGRATION_CONNECTIONSTRING; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - - apt-get update - apt-get install -y postgresql-client $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" @@ -181,6 +184,15 @@ Additionally, this step has been added to ensure database migrations are applied psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; +``` + +Last, before applying the new Kubernetes templates, these environmental variables must be set. + +```powershell +$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Require;Trust Server Certificate=true"; ``` -Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. +Finally, apply the templates. diff --git a/Console.Data.SqLite/.github/workflows/build-and-deploy.yml b/Console.Data.SqLite/.github/workflows/build-and-deploy.yml index 86a7f66c..14c0c429 100644 --- a/Console.Data.SqLite/.github/workflows/build-and-deploy.yml +++ b/Console.Data.SqLite/.github/workflows/build-and-deploy.yml @@ -2,54 +2,50 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Data.SqLite IMAGE_NAME: console.data.sqlite SERVICE_NAME: console-data-sqlite VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - SQL_NAME: nanoDb SQL_SIZE: 10Gi - SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.SQL_NAME }}.sqlite" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,69 +58,52 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + ./ - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; - name: Kubernetes Deploy shell: pwsh @@ -158,7 +137,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -166,6 +145,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Data.SqLite/.kubernetes/cronjob.yaml b/Console.Data.SqLite/.kubernetes/cronjob.yaml index aa4f8991..1dd91868 100644 --- a/Console.Data.SqLite/.kubernetes/cronjob.yaml +++ b/Console.Data.SqLite/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -54,5 +55,3 @@ spec: persistentVolumeClaim: claimName: %SERVICE_NAME%-pvc restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.SqLite/.kubernetes/data-pvc.yaml b/Console.Data.SqLite/.kubernetes/data-pvc.yaml index 0b9d4415..f3a52c3c 100644 --- a/Console.Data.SqLite/.kubernetes/data-pvc.yaml +++ b/Console.Data.SqLite/.kubernetes/data-pvc.yaml @@ -2,10 +2,11 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: %SERVICE_NAME%-data-pvc + namespace: %KUBERNETES_NAMESPACE% spec: accessModes: - ReadWriteOnce storageClassName: %SERVICE_NAME%-data-storage-class resources: requests: - storage: %DATA_SIZE% \ No newline at end of file + storage: %SQL_SIZE% \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/data-storageclass.yaml b/Console.Data.SqLite/.kubernetes/data-storageclass.yaml index 47530cdc..2f66693c 100644 --- a/Console.Data.SqLite/.kubernetes/data-storageclass.yaml +++ b/Console.Data.SqLite/.kubernetes/data-storageclass.yaml @@ -2,7 +2,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: %SERVICE_NAME%-data-storage-class -provisioner: kubernetes.io/azure-disk +provisioner: disk.csi.azure.com parameters: storageaccounttype: Standard_LRS kind: Managed diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json index 8927ff99..8593c62d 100644 --- a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json +++ b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json @@ -1,5 +1,2 @@ { - "Data": { - "StartupAction": "Migrate" - } } \ No newline at end of file diff --git a/Console.Data.SqLite/README.md b/Console.Data.SqLite/README.md index 7661aa11..69fd89ca 100644 --- a/Console.Data.SqLite/README.md +++ b/Console.Data.SqLite/README.md @@ -65,7 +65,7 @@ Configured the application with the necessary data setup. "DefaultCollation": null, "ConnectionString": "Data Source=/data/nanoDb.sqlite", "Repository": { - "UseAutoSave": false, + "UseAutoSave": true, "QueryIncludeDepth": 4 }, "Identity": null, @@ -74,13 +74,7 @@ Configured the application with the necessary data setup. } ``` -...and `appsettings.Development.json` - -```json -"Data": { - "StartupAction": "Migrate", -} -``` +> ⚠️ **Notice:** `StartupAction` is set to `migrate` in all environments. This may cause the pod to restart during the initial deployment. ## Docker Compose Added SqLite as a service dependency in `docker-compose.yml`. @@ -93,7 +87,7 @@ services: ``` ## Kubernetes -Added two additional kubernetes templates, `storageclass.yaml` and `pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. +Added two additional kubernetes templates, `data-storageclass.yaml` and `data-pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. Also, updated `cronjob.yaml` adding the volumes and volume mounts. @@ -118,34 +112,7 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: - SQL_NAME: nanoDb SQL_SIZE: 10Gi - SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.nanoDb }}.sqlite" -``` - -Additionally, this step has been added to ensure database migrations are applied. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; ``` Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml index 054564d4..fa8a17ac 100644 --- a/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml +++ b/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml @@ -2,55 +2,54 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Data.SqlServer IMAGE_NAME: console.data.sqlserver SERVICE_NAME: console-data-sqlserver VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" + DOTNET_EF_TOOLS_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} SQL_NAME: nanoDb SQL_USER: api-data-sqlserver-user - SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} - SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + SQL_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -63,57 +62,52 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; - name: Database Migration & User shell: pwsh @@ -121,26 +115,26 @@ jobs: $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "1433" $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; - dotnet ef database update ` + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` + --configuration Release ` --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { throw "error"; }; - - apt-get update - apt-get install -y mssql-tools unixodbc-dev $loginExists = sqlcmd ` -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -h -1 ` -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" @@ -150,7 +144,7 @@ jobs: -S "$env:SQL_HOST,$env:SQL_PORT" ` -U $env:SQL_ADMIN_USER ` -P $env:SQL_ADMIN_PASSWORD ` - -d master ` + -d main ` -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" }; @@ -204,7 +198,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -212,6 +206,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml b/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml index fffa83c4..9e6a2303 100644 --- a/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml +++ b/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: $env:SERVICE_NAME-sql-auth-secret + name: %SERVICE_NAME%-sql-auth-secret namespace: %KUBERNETES_NAMESPACE% type: Opaque stringData: diff --git a/Console.Data.SqlServer/.kubernetes/cronjob.yaml b/Console.Data.SqlServer/.kubernetes/cronjob.yaml index 9ae388c3..a5d8b30b 100644 --- a/Console.Data.SqlServer/.kubernetes/cronjob.yaml +++ b/Console.Data.SqlServer/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -53,5 +54,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.SqlServer/README.md b/Console.Data.SqlServer/README.md index cdae2e12..cb9b51ec 100644 --- a/Console.Data.SqlServer/README.md +++ b/Console.Data.SqlServer/README.md @@ -104,7 +104,7 @@ services: ``` ## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. +Added the `auth-sql-secret.yaml` for the connectionstring to the `cronjob.yaml`. ```json spec: @@ -126,6 +126,8 @@ Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: + DOTNET_EF_TOOLS_VERSION: "10.0" + AZURE_GROUP_DATABASE : ${{ vars.AZURE_RESOURCE_GROUP_DATABASE }} SQL_NAME: nanoDb SQL_USER: api-data-sqlserver-user SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} @@ -141,12 +143,15 @@ Additionally, this step has been added to ensure database migrations are applied $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; $env:SQL_PORT = "1433" $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; - $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + $env:DATA__CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` + --no-build ` + --configuration Release ` + --startup-project $env:APP_NAME ` + -- ` + --environment $env:ASPNETCORE_ENVIRONMENT; if ($LastExitCode -ne 0) { @@ -157,42 +162,51 @@ Additionally, this step has been added to ensure database migrations are applied apt-get install -y mssql-tools unixodbc-dev $loginExists = sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d main ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" if ($loginExists -eq 0) { sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d main ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" }; $userExists = sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d $env:SQL_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" if ($userExists -eq 0) { sqlcmd ` - -S "$env:SQL_HOST,$env:SQL_PORT" ` - -U $env:SQL_ADMIN_USER ` - -P $env:SQL_ADMIN_PASSWORD ` - -d $env:SQL_NAME ` - -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" }; + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; +``` + +Last, before applying the new Kubernetes templates, these environmental variables must be set. + +```powershell +$env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_USER;Password=$env:SQL_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; ``` -Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. +Finally, the templates can be applied. diff --git a/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml index 0233cbe7..4627dde2 100644 --- a/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml +++ b/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Eventing.RabbitMq IMAGE_NAME: console.eventing.rabbitmq SERVICE_NAME: console-eventing-rabbitmq VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml index 44ab91fe..f373d966 100644 --- a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml +++ b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -27,16 +28,26 @@ spec: image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% imagePullPolicy: Always env: - - name: Eventing__Credentials__Id - valueFrom: - secretKeyRef: - name: rabbitmq-auth - key: username - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq-auth - key: password + - name: Eventing__Credentials__Host + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: host + - name: Eventing__Credentials__Port + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: port + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: username + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: password envFrom: - configMapRef: name: %SERVICE_NAME%-config @@ -58,5 +69,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Eventing.RabbitMq/README.md b/Console.Eventing.RabbitMq/README.md index d0a49baa..3a8fd651 100644 --- a/Console.Eventing.RabbitMq/README.md +++ b/Console.Eventing.RabbitMq/README.md @@ -104,23 +104,34 @@ services: ## Kubernetes Added the `rabbitmq` secret for password to the `cronjob.yaml`. -```json +```yaml spec: - template: + jobTemplate: spec: - containers: - env: - - name: Eventing__Credentials__Id - valueFrom: - secretKeyRef: - name: rabbitmq-auth - key: username - envFrom: - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq-auth - key: password + template: + spec: + containers: + env: + - name: Eventing__Credentials__Host + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: host + - name: Eventing__Credentials__Port + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: port + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: username + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-default-user + key: password ``` > ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml b/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml index 0ad7df32..3c4d5ad2 100644 --- a/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml +++ b/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml @@ -2,50 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.ExceptionHandling IMAGE_NAME: console.exceptionhandling SERVICE_NAME: console-exceptionhandling VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -58,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -127,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -135,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.ExceptionHandling/.kubernetes/cronjob.yaml b/Console.ExceptionHandling/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.ExceptionHandling/.kubernetes/cronjob.yaml +++ b/Console.ExceptionHandling/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Localization/.github/workflows/build-and-deploy.yml b/Console.Localization/.github/workflows/build-and-deploy.yml index aacf89be..5071ea98 100644 --- a/Console.Localization/.github/workflows/build-and-deploy.yml +++ b/Console.Localization/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Localization IMAGE_NAME: console.localization SERVICE_NAME: console-localization VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Localization/.kubernetes/cronjob.yaml b/Console.Localization/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.Localization/.kubernetes/cronjob.yaml +++ b/Console.Localization/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml index 4dc97976..ee633969 100644 --- a/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml +++ b/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Logging.Log4Net IMAGE_NAME: console.logging.log4net SERVICE_NAME: console-logging-log4net VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Logging.Log4Net/.kubernetes/cronjob.yaml b/Console.Logging.Log4Net/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.Logging.Log4Net/.kubernetes/cronjob.yaml +++ b/Console.Logging.Log4Net/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml index 1c105bb3..ad5eb89c 100644 --- a/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml +++ b/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Logging.Microsoft IMAGE_NAME: console.logging.microsoft SERVICE_NAME: console-logging-microsoft VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Logging.Microsoft/.kubernetes/cronjob.yaml b/Console.Logging.Microsoft/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.Logging.Microsoft/.kubernetes/cronjob.yaml +++ b/Console.Logging.Microsoft/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.NLog/.github/workflows/build-and-deploy.yml b/Console.Logging.NLog/.github/workflows/build-and-deploy.yml index 01436524..50d2f923 100644 --- a/Console.Logging.NLog/.github/workflows/build-and-deploy.yml +++ b/Console.Logging.NLog/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Logging.NLog IMAGE_NAME: console.logging.nlog SERVICE_NAME: console-logging-nlog VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Logging.NLog/.kubernetes/cronjob.yaml b/Console.Logging.NLog/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.Logging.NLog/.kubernetes/cronjob.yaml +++ b/Console.Logging.NLog/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml index fb5412b5..a66649ba 100644 --- a/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml +++ b/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Logging.Serilog IMAGE_NAME: console.logging.serilog SERVICE_NAME: console-logging-serilog VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Logging.Serilog/.kubernetes/cronjob.yaml b/Console.Logging.Serilog/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.Logging.Serilog/.kubernetes/cronjob.yaml +++ b/Console.Logging.Serilog/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.StartupTasks/.github/workflows/build-and-deploy.yml b/Console.StartupTasks/.github/workflows/build-and-deploy.yml index 80e559e4..7d431b48 100644 --- a/Console.StartupTasks/.github/workflows/build-and-deploy.yml +++ b/Console.StartupTasks/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.StartupTasks IMAGE_NAME: console.startuptasks SERVICE_NAME: console-startuptasks VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.StartupTasks/.kubernetes/cronjob.yaml b/Console.StartupTasks/.kubernetes/cronjob.yaml index 32007e73..e10e9bd5 100644 --- a/Console.StartupTasks/.kubernetes/cronjob.yaml +++ b/Console.StartupTasks/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Storage.Azure/.github/workflows/build-and-deploy.yml b/Console.Storage.Azure/.github/workflows/build-and-deploy.yml index 140653e8..cdd6852f 100644 --- a/Console.Storage.Azure/.github/workflows/build-and-deploy.yml +++ b/Console.Storage.Azure/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Storage.Azure IMAGE_NAME: console.storage.azure SERVICE_NAME: console-storage-azure VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_GROUP_STORAGE: ${{ vars.AZURE_RESOURCE_GROUP_STORAGE }} AZURE_GROUP_BACKUP: ${{ vars.AZURE_RESOURCE_GROUP_BACKUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi @@ -37,20 +35,19 @@ env: KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-azure - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -63,26 +60,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; + - name: Build & Push Image + shell: pwsh + run: | + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Create Fileshare shell: pwsh run: | @@ -102,61 +126,97 @@ jobs: --storage-account $env:STORAGE_ACCOUNT_NAME ` --access-tier TransactionOptimized ` --quota $env:STORAGE_SIZE; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; - - az backup protection enable-for-azurefileshare ` - -g $env:AZURE_GROUP_BACKUP ` - -v $env:BACKUP_VAULT_NAME ` - -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + } + else + { + az storage share-rm update ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` - --azure-file-share $env:STORAGE_SHARE_NAME; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; } - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - if ($LastExitCode -ne 0) { throw "error"; }; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-fileshare-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + if ($LastExitCode -ne 0) { throw "error"; - }; + }; + + - name: Managed Identity & Federated Credentials + shell: pwsh + run: | + $env:IDENTITY_NAME = $env:SERVICE_NAME + "-identity"; + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + $env:KUBERNETES_ISSUER_URL = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].['oidcIssuerProfile.issuerUrl'] -o tsv; + $env:STORAGE_ACCOUNT_ID = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].id -o tsv; + + if (-not $env:IDENTITY_PRINCIPAL_ID) + { + az identity create ` + -g $env:AZURE_GROUP_KUBERNETES ` + -n $env:IDENTITY_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + } + + az role assignment create ` + --assignee-object-id $env:IDENTITY_PRINCIPAL_ID ` + --assignee-principal-type ServicePrincipal ` + --role "Storage File Data SMB MI Admin" ` + --scope $env:STORAGE_ACCOUNT_ID + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + az identity federated-credential create ` + --name $env:SERVICE_NAME-credentials ` + --resource-group $env:AZURE_GROUP_KUBERNETES ` + --identity-name $env:IDENTITY_NAME ` + --issuer $env:KUBERNETES_ISSUER_URL ` + --subject "system:serviceaccount:${env:KUBERNETES_NAMESPACE}:${env:SERVICE_NAME}-service-account" ` + --audience api://AzureADTokenExchange; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "IDENTITY_NAME=$env:IDENTITY_NAME" >> $env:GITHUB_ENV; - name: Kubernetes Deploy shell: pwsh run: | + $env:IDENTITY_CLIENT_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query clientId -o tsv; + $env:VOLUME_NAME_SUFFIX = $env:IDENTITY_CLIENT_ID.Substring(0, 5); + + Get-Content .kubernetes/service-account.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service-account.tmp.yaml; + kubectl apply -f .kubernetes/service-account.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + Get-Content .kubernetes/storage-pv.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pv.tmp.yaml; kubectl apply -f .kubernetes/storage-pv.tmp.yaml; if ($LastExitCode -ne 0) @@ -186,7 +246,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -194,6 +254,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Storage.Azure/.kubernetes/configmap.yaml b/Console.Storage.Azure/.kubernetes/configmap.yaml index f2293b0f..dbc85e41 100644 --- a/Console.Storage.Azure/.kubernetes/configmap.yaml +++ b/Console.Storage.Azure/.kubernetes/configmap.yaml @@ -5,4 +5,5 @@ metadata: namespace: %KUBERNETES_NAMESPACE% data: App__Version: %VERSION% + Storage__ShareName: %STORAGE_SHARE_NAME% DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/cronjob.yaml b/Console.Storage.Azure/.kubernetes/cronjob.yaml index ea18271f..b66eabf7 100644 --- a/Console.Storage.Azure/.kubernetes/cronjob.yaml +++ b/Console.Storage.Azure/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -11,10 +12,11 @@ spec: metadata: labels: app: %SERVICE_NAME% + azure.workload.identity/use: "true" spec: template: spec: - automountServiceAccountToken: false + serviceAccountName: %SERVICE_NAME%-service-account securityContext: runAsUser: 1000 runAsGroup: 2000 @@ -55,10 +57,8 @@ spec: volumes: - name: %SERVICE_NAME%-volume persistentVolumeClaim: - claimName: %SERVICE_NAME%-azurefile-pvc + claimName: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% - name: tmp emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Storage.Azure/.kubernetes/service-account.yaml b/Console.Storage.Azure/.kubernetes/service-account.yaml new file mode 100644 index 00000000..283750fd --- /dev/null +++ b/Console.Storage.Azure/.kubernetes/service-account.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: %SERVICE_NAME%-service-account + namespace: %KUBERNETES_NAMESPACE% + annotations: + azure.workload.identity/client-id: %IDENTITY_CLIENT_ID% \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/storage-pv.yaml b/Console.Storage.Azure/.kubernetes/storage-pv.yaml index b6281fb8..5bc60f12 100644 --- a/Console.Storage.Azure/.kubernetes/storage-pv.yaml +++ b/Console.Storage.Azure/.kubernetes/storage-pv.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: %SERVICE_NAME%-azurefile-pv + name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% spec: capacity: storage: %STORAGE_SIZE% @@ -9,9 +9,20 @@ spec: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-static + mountOptions: + - dir_mode=0777 + - file_mode=0777 + - uid=0 + - gid=0 + claimRef: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% csi: driver: file.csi.azure.com - volumeHandle: %STORAGE_SHARE_NAME% + volumeHandle: %AZURE_GROUP_STORAGE%#%STORAGE_ACCOUNT_NAME%#%STORAGE_SHARE_NAME%-%VOLUME_NAME_SUFFIX% volumeAttributes: shareName: %STORAGE_SHARE_NAME% - storageAccount: %STORAGE_ACCOUNT_NAME% \ No newline at end of file + storageAccount: %STORAGE_ACCOUNT_NAME% + resourceGroup: %AZURE_GROUP_STORAGE% + clientID: %IDENTITY_CLIENT_ID% + mountWithWorkloadIdentityToken: "true" \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/storage-pvc.yaml b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml index 252c7857..8ce63544 100644 --- a/Console.Storage.Azure/.kubernetes/storage-pvc.yaml +++ b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: %SERVICE_NAME%-azurefile-pvc + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% namespace: %KUBERNETES_NAMESPACE% spec: accessModes: @@ -9,4 +9,5 @@ spec: storageClassName: azurefile-static resources: requests: - storage: %STORAGE_SIZE% \ No newline at end of file + storage: %STORAGE_SIZE% + volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure.sln b/Console.Storage.Azure/Console.Storage.Azure.sln index 63806ffa..3d54515f 100644 --- a/Console.Storage.Azure/Console.Storage.Azure.sln +++ b/Console.Storage.Azure/Console.Storage.Azure.sln @@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes" ProjectSection(SolutionItems) = preProject .kubernetes\configmap.yaml = .kubernetes\configmap.yaml .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + .kubernetes\service-account.yaml = .kubernetes\service-account.yaml .kubernetes\storage-pv.yaml = .kubernetes\storage-pv.yaml .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml EndProjectSection diff --git a/Console.Storage.Azure/README.md b/Console.Storage.Azure/README.md index 3731aeef..b3bfe794 100644 --- a/Console.Storage.Azure/README.md +++ b/Console.Storage.Azure/README.md @@ -52,39 +52,105 @@ Configured the application with the necessary storage setup. Mapped the fileshare in `docker-compose.yml`. ```yaml -docker +services: + svc.accounts: volumes: - ./bin/nano-storage-azure:/mnt/nano-storage-azure ``` ## Kubernetes -Added two new kubernetes templaets, the `storage-pv.yaml` and `storage-pvc.yaml`. Updated the `cronjob.yaml` mounting the volume. +Added three new kubernetes templaets, the `storage-pv.yaml`, `storage-pvc.yaml`, and the `service-account.yaml`. ```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% spec: - template: + capacity: + storage: %STORAGE_SIZE% + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: azurefile-static + mountOptions: + - dir_mode=0777 + - file_mode=0777 + - uid=0 + - gid=0 + claimRef: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% + csi: + driver: file.csi.azure.com + volumeHandle: %AZURE_GROUP_STORAGE%#%STORAGE_ACCOUNT_NAME%#%STORAGE_SHARE_NAME%-%VOLUME_NAME_SUFFIX% + volumeAttributes: + shareName: %STORAGE_SHARE_NAME% + storageAccount: %STORAGE_ACCOUNT_NAME% + resourceGroup: %AZURE_GROUP_STORAGE% + clientID: %IDENTITY_CLIENT_ID% + mountWithWorkloadIdentityToken: "true" +``` + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + namespace: %KUBERNETES_NAMESPACE% +spec: + accessModes: + - ReadWriteMany + storageClassName: azurefile-static + resources: + requests: + storage: %STORAGE_SIZE% + volumeName: %SERVICE_NAME%-azurefile-pv-%VOLUME_NAME_SUFFIX% +``` + +Also, the `configmap.yaml` should have the share-name included. + +``` +data: + Storage__ShareName: %STORAGE_SHARE_NAME% +``` + +Updated the `cronjob.yaml` mounting the volume. + +```yaml +spec: + jobTemplate: + metadata: + labels: + azure.workload.identity/use: "true" spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-azurefile-pvc - - name: tmp - emptyDir: {} + template: + spec: + serviceAccountName: %SERVICE_NAME%-service-account + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-azurefile-pvc-%VOLUME_NAME_SUFFIX% + - name: tmp + emptyDir: {} ``` +Notice the name suffix for `claimName`. The PersistentVolume (PV) and PersistentVolumeClaim (PVC) are created with a suffix to allow the managed identity used for authenticating with the +Azure File Share to be changed without trying to replace existing immutable resources. + ## GitHub Actions Add the following environment variables to the `buid-and-deply.yml`. ```yaml env: - AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} - AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} + AZURE_GROUP_BACKUP: ${{ vars.AZURE_RESOURCE_GROUP_BACKUP }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_RESOURCE_GROUP_STORAGE }} STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-azure ``` @@ -95,9 +161,9 @@ Additionally, this step has been added to ensure the file share is created befor - name: Create Fileshare shell: pwsh run: | - $env:STORAGE_ACCOUNT_NAME = sudo az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + $env:STORAGE_ACCOUNT_NAME = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; - $env:FILE_SHARE_EXISTS = sudo az storage share-rm exists ` + $env:FILE_SHARE_EXISTS = az storage share-rm exists ` -g $env:AZURE_GROUP_STORAGE ` -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` @@ -105,30 +171,100 @@ Additionally, this step has been added to ensure the file share is created befor if ($env:FILE_SHARE_EXISTS -eq "false") { - sudo az storage share-rm create ` + az storage share-rm create ` -g $env:AZURE_GROUP_STORAGE ` -n $env:STORAGE_SHARE_NAME ` --storage-account $env:STORAGE_ACCOUNT_NAME ` --access-tier TransactionOptimized ` --quota $env:STORAGE_SIZE; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; + } + else + { + az storage share-rm update ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + } + + if ($LastExitCode -ne 0) + { + throw "error"; + }; - $env:BACKUP_VAULT_NAME = sudo az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; - sudo az backup protection enable-for-azurefileshare ` - -g $env:AZURE_GROUP_BACKUP ` - -v $env:BACKUP_VAULT_NAME ` - -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` - --storage-account $env:STORAGE_ACCOUNT_NAME ` - --azure-file-share $env:STORAGE_SHARE_NAME; - - if ($LastExitCode -ne 0) - { + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-fileshare-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; +``` + +...and includes a step to create a managed identity and federated credentials for authenticating with the storage account. + +``` +- name: Managed Identity & Federated Credentials + shell: pwsh + run: | + $env:IDENTITY_NAME = $env:SERVICE_NAME + "-identity"; + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; + $env:KUBERNETES_ISSUER_URL = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].['oidcIssuerProfile.issuerUrl'] -o tsv; + $env:STORAGE_ACCOUNT_ID = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].id -o tsv; + + if (-not $env:IDENTITY_PRINCIPAL_ID) + { + az identity create ` + -g $env:AZURE_GROUP_KUBERNETES ` + -n $env:IDENTITY_NAME; + + if ($LastExitCode -ne 0) + { throw "error"; }; + + $env:IDENTITY_PRINCIPAL_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query principalId -o tsv; } -``` \ No newline at end of file + + az role assignment create ` + --assignee-object-id $env:IDENTITY_PRINCIPAL_ID ` + --assignee-principal-type ServicePrincipal ` + --role "Storage File Data SMB MI Admin" ` + --scope $env:STORAGE_ACCOUNT_ID + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + az identity federated-credential create ` + --name $env:SERVICE_NAME-credentials ` + --resource-group $env:AZURE_GROUP_KUBERNETES ` + --identity-name $env:IDENTITY_NAME ` + --issuer $env:KUBERNETES_ISSUER_URL ` + --subject "system:serviceaccount:${env:KUBERNETES_NAMESPACE}:${env:SERVICE_NAME}-service-account" ` + --audience api://AzureADTokenExchange; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo "IDENTITY_NAME=$env:IDENTITY_NAME" >> $env:GITHUB_ENV; +``` + +Last, during the Kubernetes deployment step, before any resources are applied, environmental variables required for the new `stoerage-pv.yaml` and `stoerage-pvc.yaml` must be set. + +```powershell +$env:IDENTITY_CLIENT_ID = az identity show -g $env:AZURE_GROUP_KUBERNETES -n $env:IDENTITY_NAME --query clientId -o tsv; +$env:VOLUME_NAME_SUFFIX = $env:IDENTITY_CLIENT_ID.Substring(0, 5); +``` + +The deployment commands have been updated to apply the new Kubernetes storage templates. diff --git a/Console.Storage.Local/.github/workflows/build-and-deploy.yml b/Console.Storage.Local/.github/workflows/build-and-deploy.yml index 236021f4..6cb61dd1 100644 --- a/Console.Storage.Local/.github/workflows/build-and-deploy.yml +++ b/Console.Storage.Local/.github/workflows/build-and-deploy.yml @@ -2,31 +2,29 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Storage.Local IMAGE_NAME: console.storage.local SERVICE_NAME: console-storage-local VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi @@ -35,20 +33,19 @@ env: KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" STORAGE_SIZE: 1000 STORAGE_SHARE_NAME: nano-storage-local - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -61,57 +58,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -144,7 +137,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -152,6 +145,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Storage.Local/.kubernetes/configmap.yaml b/Console.Storage.Local/.kubernetes/configmap.yaml index f2293b0f..dbc85e41 100644 --- a/Console.Storage.Local/.kubernetes/configmap.yaml +++ b/Console.Storage.Local/.kubernetes/configmap.yaml @@ -5,4 +5,5 @@ metadata: namespace: %KUBERNETES_NAMESPACE% data: App__Version: %VERSION% + Storage__ShareName: %STORAGE_SHARE_NAME% DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/cronjob.yaml b/Console.Storage.Local/.kubernetes/cronjob.yaml index 0604cbad..514379a1 100644 --- a/Console.Storage.Local/.kubernetes/cronjob.yaml +++ b/Console.Storage.Local/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -51,13 +52,9 @@ spec: mountPath: /mnt/%STORAGE_SHARE_NAME% - name: tmp mountPath: /tmp - restartPolicy: OnFailure volumes: - - name: %STORAGE_SHARE_NAME% - emptyDir: {} + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc - name: tmp emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - - diff --git a/Console.Storage.Local/.kubernetes/storage-storageclass.yaml b/Console.Storage.Local/.kubernetes/storage-storageclass.yaml index f9ccaa3e..fa2c6dd3 100644 --- a/Console.Storage.Local/.kubernetes/storage-storageclass.yaml +++ b/Console.Storage.Local/.kubernetes/storage-storageclass.yaml @@ -2,7 +2,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: %SERVICE_NAME%-storage-class -provisioner: kubernetes.io/azure-disk +provisioner: disk.csi.azure.com parameters: storageaccounttype: Standard_LRS kind: Managed diff --git a/Console.Storage.Local/README.md b/Console.Storage.Local/README.md index f8b0ac76..4e93cfd3 100644 --- a/Console.Storage.Local/README.md +++ b/Console.Storage.Local/README.md @@ -51,7 +51,8 @@ Configured the application with the necessary storage setup. Mapped the fileshare in `docker-compose.yml`. ```yaml -docker +services: + console.storage.local: volumes: - ./bin/nano-storage-local:/mnt/nano-storage-local ``` @@ -63,20 +64,22 @@ Also, updated `cronjob.yaml` adding the volumes and volume mounts. ```yaml spec: - template: + jobTemplate: spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - - name: tmp - emptyDir: {} + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + - name: tmp + emptyDir: {} ``` ## GitHub Actions @@ -89,11 +92,3 @@ env: ``` Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Console.Workers/.github/workflows/build-and-deploy.yml b/Console.Workers/.github/workflows/build-and-deploy.yml index 16aadd83..ded38bde 100644 --- a/Console.Workers/.github/workflows/build-and-deploy.yml +++ b/Console.Workers/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Workers IMAGE_NAME: console.workers SERVICE_NAME: console-workers VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console.Workers/.kubernetes/cronjob.yaml b/Console.Workers/.kubernetes/cronjob.yaml index 503cd99a..cbd97e65 100644 --- a/Console.Workers/.kubernetes/cronjob.yaml +++ b/Console.Workers/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,7 +48,5 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console._Blank/.github/workflows/build-and-deploy.yml b/Console._Blank/.github/workflows/build-and-deploy.yml index 4a3c8b1d..88a23e95 100644 --- a/Console._Blank/.github/workflows/build-and-deploy.yml +++ b/Console._Blank/.github/workflows/build-and-deploy.yml @@ -2,51 +2,48 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Console.Blank IMAGE_NAME: console.blank SERVICE_NAME: console-blank VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} KUBERNETES_REPLICA_HISTORY_COUNT: 4 KUBERNETES_MEMORY_REQUEST: 256Mi KUBERNETES_MEMORY_LIMIT: 768Mi KUBERNETES_CPU_REQUEST: 50m KUBERNETES_CPU_LIMIT: 150m KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: + packages: read contents: write - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -59,57 +56,53 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { - throw "error"; + throw "error"; }; - - name: Publish Image + - name: Build & Push Image shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` --build-arg NUGET_HOST=$env:NUGET_HOST ` --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + ./ - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | @@ -128,7 +121,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -136,6 +129,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Console._Blank/.kubernetes/cronjob.yaml b/Console._Blank/.kubernetes/cronjob.yaml index 71ff564c..e10e9bd5 100644 --- a/Console._Blank/.kubernetes/cronjob.yaml +++ b/Console._Blank/.kubernetes/cronjob.yaml @@ -2,6 +2,7 @@ apiVersion: batch/v1 kind: CronJob metadata: name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% spec: schedule: %KUBERNETES_CRONJOB_SCHEDULE% concurrencyPolicy: Forbid @@ -47,5 +48,3 @@ spec: drop: - ALL restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Web._Blank/.github/workflows/build-and-deploy.yml b/Web._Blank/.github/workflows/build-and-deploy.yml index ec5d0318..a9caa035 100644 --- a/Web._Blank/.github/workflows/build-and-deploy.yml +++ b/Web._Blank/.github/workflows/build-and-deploy.yml @@ -2,33 +2,31 @@ name: Build And Deploy on: pull_request: branches: - - master + - main push: branches: - - master + - main + workflow_dispatch: env: APP_NAME: Web.Blank IMAGE_NAME: web.blank SERVICE_NAME: web-blank VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 + DOTNET_SDK_VERSION: "10.0" + DOTNET_ASPNET_VERSION: "10.0" AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DELIVERY: ${{ vars.AZURE_RESOURCE_GROUP_DELIVERY }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json NUGET_USERNAME: ${{ github.actor }} NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_HOST: ghcr.io - CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: apps - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_NAMESPACE: ${{ vars.KUBERNETES_NAMESPACE }} + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/main' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/main' && 8 || 5 }} KUBERNETES_REPLICA_HISTORY_COUNT: 0 KUBERNETES_MEMORY_REQUEST: 512Mi KUBERNETES_MEMORY_LIMIT: 1536Mi @@ -36,20 +34,19 @@ env: KUBERNETES_CPU_REQUEST: 200m KUBERNETES_CPU_LIMIT: 600m KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} +concurrency: + group: ${{ github.workflow }}-${{ github.repository }} + cancel-in-progress: true jobs: build-and-deploy: runs-on: - self-hosted - linux - - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + - ${{ github.ref == 'refs/heads/main' && 'Production' || 'Staging' }} permissions: contents: write packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true steps: - uses: actions/checkout@v6 @@ -62,72 +59,69 @@ jobs: $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - name: Build + - name: Build Solution shell: pwsh run: | dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Test + - name: Test Solution shell: pwsh run: | dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) { throw "error"; }; - - name: Publish Image + - name: Publish NuGet shell: pwsh run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; if ($LastExitCode -ne 0) { throw "error"; }; - echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; - docker push $imageLatestTag; - docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet + - name: Build & Push Image shell: pwsh run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + $env:CONTAINER_REGISTRY_HOST = az acr list -g $env:AZURE_GROUP_DELIVERY --query [0].loginServer -o tsv; + + az acr build ` + --registry $env:CONTAINER_REGISTRY_HOST ` + --agent-pool buildpool ` + -t "$($env:IMAGE_NAME):latest" ` + -t "$($env:IMAGE_NAME):$($env:VERSION)" ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=https://github.com/$env:GITHUB_REPOSITORY ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./ + if ($LastExitCode -ne 0) { throw "error"; }; + echo "CONTAINER_REGISTRY_HOST=$env:CONTAINER_REGISTRY_HOST" >> $env:GITHUB_ENV; + - name: Kubernetes Deploy shell: pwsh run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; kubectl apply -f .kubernetes/service.tmp.yaml; if ($LastExitCode -ne 0) { @@ -156,7 +150,7 @@ jobs: }; - name: GitHub Release - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' uses: ncipollo/release-action@v1 with: tag: v${{ env.VERSION }} @@ -164,6 +158,8 @@ jobs: body: | Version: ${{ env.VERSION }} Commit: ${{ github.sha }} + Build run: ${{ github.run_number }} + Image: ${{ env.CONTAINER_REGISTRY_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }} artifacts: "nupkgs/*" token: ${{ secrets.GITHUB_TOKEN }} draft: false diff --git a/Web._Blank/.kubernetes/configmap.yaml b/Web._Blank/.kubernetes/configmap.yaml index 3977c0ee..d13013d1 100644 --- a/Web._Blank/.kubernetes/configmap.yaml +++ b/Web._Blank/.kubernetes/configmap.yaml @@ -1,4 +1,4 @@ - apiVersion: v1 +apiVersion: v1 kind: ConfigMap metadata: name: %SERVICE_NAME%-config diff --git a/Web._Blank/.kubernetes/deployment.yaml b/Web._Blank/.kubernetes/deployment.yaml index 02882c56..9871912e 100644 --- a/Web._Blank/.kubernetes/deployment.yaml +++ b/Web._Blank/.kubernetes/deployment.yaml @@ -73,6 +73,3 @@ spec: periodSeconds: 5 initialDelaySeconds: 20 timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Web._Blank/Dockerfile b/Web._Blank/Dockerfile index 119abdb6..7be1dee3 100644 --- a/Web._Blank/Dockerfile +++ b/Web._Blank/Dockerfile @@ -29,6 +29,10 @@ RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . + +RUN mkdir -p /app/wwwroot + +ENV ASPNETCORE_HTTP_PORTS= ENV COMPlus_EnableDiagnostics=0 ENV DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj b/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj index df35117b..c8a0992a 100644 --- a/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj +++ b/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj @@ -21,17 +21,11 @@ Michael Vivet $(ProjectName) Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - + A practical example highlighting a specific area or scenario of Nano for learning and experimentation. Shows how features or patterns can be applied without representing a full application or complete reference. + git - master - https://github.com/Nano-Core/Nano.Examples.git + main + https://github.com/Nano-Core/Nano.Lessons.git $(GitCommitHash) true true @@ -40,7 +34,7 @@ true $(Version) nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) + https://github.com/Nano-Core/Nano.Lessons/$(ProjectName) LICENSE icon.png README.md diff --git a/Web._Blank/Web.Blank/Dockerfile.Local b/Web._Blank/Web.Blank/Dockerfile.Local index 207af714..ccb45e06 100644 --- a/Web._Blank/Web.Blank/Dockerfile.Local +++ b/Web._Blank/Web.Blank/Dockerfile.Local @@ -3,4 +3,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base EXPOSE 8080 +ENV ASPNETCORE_HTTP_PORTS= + ENTRYPOINT ["dotnet", "Web.Blank.dll"] \ No newline at end of file