Main #278
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Main | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - "application/*" | |
| - "application/shared-kernel/**" | |
| - "application/shared-webapp/**" | |
| - "application/main/**" | |
| - ".github/workflows/main.yml" | |
| - ".github/workflows/_deploy-container.yml" | |
| - ".github/workflows/_migrate-database.yml" | |
| - "!**.md" | |
| pull_request: | |
| paths: | |
| - "application/*" | |
| - "application/shared-kernel/**" | |
| - "application/shared-webapp/**" | |
| - "application/main/**" | |
| - ".github/workflows/main.yml" | |
| - ".github/workflows/_deploy-container.yml" | |
| - ".github/workflows/_migrate-database.yml" | |
| - "!**.md" | |
| workflow_dispatch: | |
| concurrency: | |
| group: main-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| id-token: write | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| build-and-test: | |
| name: Build and Test | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| version: ${{ steps.generate_version.outputs.version }} | |
| deploy_staging: ${{ steps.determine_deployment.outputs.deploy_staging }} | |
| deploy_production: ${{ steps.determine_deployment.outputs.deploy_production }} | |
| migrations_changed: ${{ steps.detect_migrations.outputs.migrations_changed }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v7 | |
| with: | |
| # Full history is required to diff base..head for migration-change detection. | |
| fetch-depth: 0 | |
| - name: Detect Migration Changes | |
| id: detect_migrations | |
| env: | |
| BASE_REF: ${{ github.event.pull_request.base.sha || format('{0}~1', github.sha) }} | |
| HEAD_REF: ${{ github.event.pull_request.head.sha || github.sha }} | |
| run: | | |
| if git diff --name-only "$BASE_REF" "$HEAD_REF" | grep -q "^application/main/Core/Database/Migrations/"; then | |
| echo "migrations_changed=true" >> $GITHUB_OUTPUT | |
| echo "Migration changes detected — Database Staging will run." | |
| else | |
| echo "migrations_changed=false" >> $GITHUB_OUTPUT | |
| echo "No migration changes — Database Staging will be skipped on pull requests." | |
| fi | |
| - name: Generate Version | |
| id: generate_version | |
| run: | | |
| # Zero-padded so versions sort lexicographically and clock-time is unambiguous (e.g. 0904, not 94) | |
| VERSION=$(date +"%Y.%m.%d.%H%M") | |
| echo "Generated version: $VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Determine Deployment Conditions | |
| id: determine_deployment | |
| run: | | |
| deploy_staging="${{ github.ref == 'refs/heads/main' && vars.STAGING_CLUSTER_ENABLED == 'true' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Deploy to Staging')) }}" | |
| echo "deploy_staging=$deploy_staging" >> $GITHUB_OUTPUT | |
| deploy_production="${{ github.ref == 'refs/heads/main' && vars.PRODUCTION_CLUSTER1_ENABLED == 'true' }}" | |
| echo "deploy_production=$deploy_production" >> $GITHUB_OUTPUT | |
| - name: Setup Node.js Environment | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| - name: Install Node Modules | |
| working-directory: application | |
| run: npm ci | |
| - name: Setup .NET Core SDK | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| global-json-file: application/global.json | |
| - name: Restore .NET Tools | |
| working-directory: application | |
| run: dotnet tool restore | |
| - name: Restore .NET Dependencies | |
| working-directory: application | |
| run: dotnet restore | |
| - name: Generate and Set User Secret for Token Signing Key | |
| working-directory: application/shared-kernel/SharedKernel | |
| run: | | |
| # Extract UserSecretsId from the .csproj file | |
| USER_SECRETS_ID=$(grep -oP '(?<=<UserSecretsId>).*?(?=</UserSecretsId>)' SharedKernel.csproj) | |
| # Generate a 512-bit key and set it as a user secret that can be use for token signing when running tests | |
| dotnet user-secrets set "authentication-token-signing-key" "$(openssl rand -base64 64)" --id $USER_SECRETS_ID | |
| - name: Build Email Templates | |
| working-directory: application | |
| run: npx turbo run build --filter=@repo/emails | |
| - name: Run Tests | |
| working-directory: application | |
| run: | | |
| dotnet build main/Main.slnf --no-restore /p:Version=${{ steps.generate_version.outputs.version }} /p:DeploymentCommitHash=${{ github.event.pull_request.head.sha || github.sha }} /p:DeploymentGithubActionId=${{ github.run_id }} && | |
| dotnet test main/Main.slnf --no-build | |
| - name: Save Backend Build Artifacts for Migration Plan | |
| if: ${{ vars.STAGING_CLUSTER_ENABLED == 'true' && (github.event_name != 'pull_request' || steps.detect_migrations.outputs.migrations_changed == 'true') }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: main-build | |
| path: | | |
| application/**/bin | |
| application/**/obj | |
| retention-days: 1 | |
| - name: Build Frontend Artifacts | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| working-directory: application | |
| run: npm run build | |
| - name: Publish Frontend Artifacts | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| working-directory: application/main/WebApp | |
| run: npm run publish | |
| - name: Publish API Build | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| working-directory: application/main | |
| run: | | |
| dotnet publish ./Api/Main.Api.csproj --configuration Release --no-restore --output ./Api/publish /p:Version=${{ steps.generate_version.outputs.version }} /p:DeploymentCommitHash=${{ github.event.pull_request.head.sha || github.sha }} /p:DeploymentGithubActionId=${{ github.run_id }} | |
| - name: Save API Artifacts | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: main-api | |
| path: application/main/Api/publish/**/* | |
| - name: Publish Workers Build | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| working-directory: application/main | |
| run: | | |
| dotnet publish ./Workers/Main.Workers.csproj --configuration Release --no-restore --output ./Workers/publish /p:Version=${{ steps.generate_version.outputs.version }} /p:DeploymentCommitHash=${{ github.event.pull_request.head.sha || github.sha }} /p:DeploymentGithubActionId=${{ github.run_id }} | |
| - name: Save Workers Artifacts | |
| if: ${{ steps.determine_deployment.outputs.deploy_staging == 'true' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: main-workers | |
| path: application/main/Workers/publish/**/* | |
| database-migrations-stage: | |
| name: Database Staging | |
| if: ${{ vars.STAGING_CLUSTER_ENABLED == 'true' && (github.event_name != 'pull_request' || needs.build-and-test.outputs.migrations_changed == 'true') }} | |
| needs: build-and-test | |
| uses: ./.github/workflows/_migrate-database.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "stage" | |
| cluster_location_acronym: ${{ vars.STAGING_CLUSTER_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.STAGING_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.STAGING_SUBSCRIPTION_ID }} | |
| database_name: main | |
| relative_project_path: main/Core/Main.csproj | |
| relative_startup_project: main/Api/Main.Api.csproj | |
| db_context: MainDbContext | |
| apply_migrations: ${{ needs.build-and-test.outputs.deploy_staging == 'true' }} | |
| build_artifact_name: main-build | |
| api-stage: | |
| name: API Staging | |
| # !cancelled() keeps this from auto-skipping when database-migrations-stage is skipped (it is, on | |
| # pull requests without migration changes); the result guards still block on a real failure. | |
| if: ${{ !cancelled() && needs.build-and-test.result == 'success' && needs.database-migrations-stage.result != 'failure' && needs.build-and-test.outputs.deploy_staging == 'true' }} | |
| needs: [build-and-test, database-migrations-stage] | |
| uses: ./.github/workflows/_deploy-container.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "stage" | |
| cluster_location_acronym: ${{ vars.STAGING_CLUSTER_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.STAGING_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.STAGING_SUBSCRIPTION_ID }} | |
| image_name: main-api | |
| version: ${{ needs.build-and-test.outputs.version }} | |
| artifacts_name: main-api | |
| artifacts_path: application/main/Api/publish | |
| docker_context: ./application/main | |
| docker_file: ./Api/Dockerfile | |
| workers-stage: | |
| name: Workers Staging | |
| # !cancelled() keeps this from auto-skipping when database-migrations-stage is skipped (it is, on | |
| # pull requests without migration changes); the result guards still block on a real failure. | |
| if: ${{ !cancelled() && needs.build-and-test.result == 'success' && needs.database-migrations-stage.result != 'failure' && needs.build-and-test.outputs.deploy_staging == 'true' }} | |
| needs: [build-and-test, database-migrations-stage] | |
| uses: ./.github/workflows/_deploy-container.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "stage" | |
| cluster_location_acronym: ${{ vars.STAGING_CLUSTER_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.STAGING_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.STAGING_SUBSCRIPTION_ID }} | |
| image_name: main-workers | |
| version: ${{ needs.build-and-test.outputs.version }} | |
| artifacts_name: main-workers | |
| artifacts_path: application/main/Workers/publish | |
| docker_context: ./application/main | |
| docker_file: ./Workers/Dockerfile | |
| database-migrations-prod1: | |
| name: Database Production | |
| if: ${{ needs.build-and-test.outputs.deploy_production == 'true' }} | |
| needs: [build-and-test, api-stage, workers-stage] | |
| uses: ./.github/workflows/_migrate-database.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "prod" | |
| cluster_location_acronym: ${{ vars.PRODUCTION_CLUSTER1_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.PRODUCTION_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.PRODUCTION_SUBSCRIPTION_ID }} | |
| database_name: main | |
| relative_project_path: main/Core/Main.csproj | |
| relative_startup_project: main/Api/Main.Api.csproj | |
| db_context: MainDbContext | |
| apply_migrations: true | |
| build_artifact_name: main-build | |
| api-prod1: | |
| name: API Production | |
| if: ${{ needs.build-and-test.outputs.deploy_production == 'true' }} | |
| needs: [build-and-test, database-migrations-prod1] | |
| uses: ./.github/workflows/_deploy-container.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "prod" | |
| cluster_location_acronym: ${{ vars.PRODUCTION_CLUSTER1_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.PRODUCTION_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.PRODUCTION_SUBSCRIPTION_ID }} | |
| image_name: main-api | |
| version: ${{ needs.build-and-test.outputs.version }} | |
| artifacts_name: main-api | |
| artifacts_path: application/main/Api/publish | |
| docker_context: ./application/main | |
| docker_file: ./Api/Dockerfile | |
| workers-prod1: | |
| name: Workers Production | |
| if: ${{ needs.build-and-test.outputs.deploy_production == 'true' }} | |
| needs: [build-and-test, database-migrations-prod1] | |
| uses: ./.github/workflows/_deploy-container.yml | |
| secrets: inherit | |
| with: | |
| azure_environment: "prod" | |
| cluster_location_acronym: ${{ vars.PRODUCTION_CLUSTER1_LOCATION_ACRONYM }} | |
| service_principal_id: ${{ vars.PRODUCTION_SERVICE_PRINCIPAL_ID }} | |
| subscription_id: ${{ vars.PRODUCTION_SUBSCRIPTION_ID }} | |
| image_name: main-workers | |
| version: ${{ needs.build-and-test.outputs.version }} | |
| artifacts_name: main-workers | |
| artifacts_path: application/main/Workers/publish | |
| docker_context: ./application/main | |
| docker_file: ./Workers/Dockerfile |