diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 70328c8710..f3e82ef9ae 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -30,6 +30,9 @@ on: pull_request_number: description: 'Pull Request Number' required: true + pull_request_merge_sha: + description: 'Pull Request Merge SHA' + required: true env: TGS_DOTNET_VERSION: 8 @@ -47,8 +50,8 @@ concurrency: cancel-in-progress: true jobs: - ci-start-gate: - name: CI Start Gate + release-notes-build: + name: Build Release Notes for Other Jobs runs-on: ubuntu-latest steps: - name: Setup dotnet @@ -75,7 +78,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -103,12 +106,8 @@ jobs: name: release_notes_bins path: ./release_notes_bins/ - - name: Set CI Check Run (Started) - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Started ${{ github.run_id }} - code-scanning: name: Code Scanning - needs: ci-start-gate runs-on: ubuntu-latest permissions: security-events: write @@ -140,7 +139,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -178,7 +177,6 @@ jobs: dmapi-build: name: Build DMAPI - needs: ci-start-gate strategy: fail-fast: false matrix: @@ -246,7 +244,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -284,7 +282,6 @@ jobs: opendream-build: name: Build DMAPI (OpenDream) - needs: ci-start-gate strategy: fail-fast: false matrix: @@ -323,7 +320,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -369,7 +366,6 @@ jobs: efcore-version-match: name: Check Nuget Versions Match Tools - needs: ci-start-gate runs-on: ubuntu-latest steps: - name: Checkout (Branch) @@ -390,7 +386,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -450,7 +446,7 @@ jobs: pages-build: name: Build gh-pages - needs: ci-start-gate + needs: release-notes-build runs-on: ubuntu-latest steps: - name: Setup dotnet @@ -515,7 +511,6 @@ jobs: docker-build: name: Build Docker Image - needs: ci-start-gate runs-on: ubuntu-latest env: TGS_TELEMETRY_KEY_FILE: tgs_telemetry_key.txt @@ -538,7 +533,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -567,7 +562,6 @@ jobs: linux-unit-tests: name: Linux Tests - needs: ci-start-gate strategy: fail-fast: false matrix: @@ -613,7 +607,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -663,7 +657,6 @@ jobs: windows-unit-tests: name: Windows Tests - needs: ci-start-gate strategy: fail-fast: false matrix: @@ -703,7 +696,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -861,7 +854,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1109,7 +1102,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1221,7 +1214,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1269,7 +1262,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1527,7 +1520,6 @@ jobs: build-deb: name: Build .deb Package # Can't do i386 due to https://github.com/dotnet/core/issues/4595 - needs: ci-start-gate runs-on: ubuntu-latest env: TGS_TELEMETRY_KEY_FILE: /tmp/tgs_telemetry_key.txt @@ -1582,7 +1574,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1666,7 +1658,6 @@ jobs: build-msi: name: Build Windows Installer .exe - needs: ci-start-gate runs-on: windows-latest env: TGS_TELEMETRY_KEY_FILE: C:/tgs_telemetry_key.txt @@ -1705,7 +1696,7 @@ jobs: - name: Abort if PR Merge SHA has Changed uses: actions/github-script@v7 - if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != github.sha + if: github.event_name != 'push' && github.event_name != 'schedule' && steps.get-pr-sha.outputs.head_sha != inputs.pull_request_merge_sha with: script: | const delay = ms => new Promise(res => setTimeout(res, ms)); @@ -1839,7 +1830,7 @@ jobs: check-winget-pr-template: name: Check winget-pkgs Pull Request Template is up to date - needs: ci-start-gate + needs: release-notes-build runs-on: ubuntu-latest steps: - name: Setup dotnet @@ -1864,38 +1855,12 @@ jobs: run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --winget-template-check ${{ steps.get-sha.outputs.pr_template_sha }} ci-completion-gate: - name: CI Completion Gate - needs: [ pages-build, docker-build, build-deb, build-msi, validate-openapi-spec, upload-code-coverage, check-winget-pr-template, code-scanning, efcore-version-match, ci-start-gate ] + name: CI Completion Gate # Used as a branch protection ruleset target + needs: [ pages-build, docker-build, build-deb, build-msi, validate-openapi-spec, upload-code-coverage, check-winget-pr-template, code-scanning, efcore-version-match ] runs-on: ubuntu-latest - if: always() && needs.ci-start-gate.result == 'success' steps: - - name: Setup dotnet - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '${{ env.TGS_DOTNET_VERSION }}.0.x' - dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} - - - name: Retrieve ReleaseNotes Binaries - uses: actions/download-artifact@v4 - with: - name: release_notes_bins - path: release_notes_bins - - - name: Update CI Check Run (Cancelled) - if: needs.pages-build.result == 'cancelled' || needs.docker-build.result == 'cancelled' || needs.build-deb.result == 'cancelled' || needs.build-msi.result == 'cancelled' || needs.validate-openapi-spec.result == 'cancelled' || needs.upload-code-coverage.result == 'cancelled' || needs.check-winget-pr-template.result == 'cancelled' || needs.code-scanning.result == 'cancelled' || needs.efcore-version-match.result == 'cancelled' || needs.pages-build.result == 'cancelled' - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Cancelled ${{ github.run_id }} - - - name: Update CI Check Run (Failure) - if: (!(needs.pages-build.result == 'cancelled' || needs.docker-build.result == 'cancelled' || needs.build-deb.result == 'cancelled' || needs.build-msi.result == 'cancelled' || needs.validate-openapi-spec.result == 'cancelled' || needs.upload-code-coverage.result == 'cancelled' || needs.check-winget-pr-template.result == 'cancelled' || needs.code-scanning.result == 'cancelled' || needs.efcore-version-match.result == 'cancelled' || needs.pages-build.result == 'cancelled') && (needs.pages-build.result == 'failure' || needs.docker-build.result == 'failure' || needs.build-deb.result == 'failure' || needs.build-msi.result == 'failure' || needs.validate-openapi-spec.result == 'failure' || needs.upload-code-coverage.result == 'failure' || needs.check-winget-pr-template.result == 'failure' || needs.code-scanning.result == 'failure' || needs.efcore-version-match.result == 'failure' || needs.pages-build.result == 'failure')) - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Failure ${{ github.run_id }} - - - name: Update CI Check Run (Success) - if: needs.pages-build.result == 'success' && needs.docker-build.result == 'success' && needs.build-deb.result == 'success' && needs.build-msi.result == 'success' && needs.validate-openapi-spec.result == 'success' && needs.upload-code-coverage.result == 'success' && needs.check-winget-pr-template.result == 'success' && needs.code-scanning.result == 'success' && needs.efcore-version-match.result == 'success' && needs.pages-build.result == 'success' - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Success ${{ github.run_id }} - - - name: Fail Job if Prerequisites Failed - if: (!(needs.pages-build.result == 'cancelled' || needs.docker-build.result == 'cancelled' || needs.build-deb.result == 'cancelled' || needs.build-msi.result == 'cancelled' || needs.validate-openapi-spec.result == 'cancelled' || needs.upload-code-coverage.result == 'cancelled' || needs.check-winget-pr-template.result == 'cancelled' || needs.code-scanning.result == 'cancelled' || needs.efcore-version-match.result == 'cancelled' || needs.pages-build.result == 'cancelled') && (needs.pages-build.result == 'failure' || needs.docker-build.result == 'failure' || needs.build-deb.result == 'failure' || needs.build-msi.result == 'failure' || needs.validate-openapi-spec.result == 'failure' || needs.upload-code-coverage.result == 'failure' || needs.check-winget-pr-template.result == 'failure' || needs.code-scanning.result == 'failure' || needs.efcore-version-match.result == 'failure' || needs.pages-build.result == 'failure')) - run: exit 1 + - name: Mandatory Empty Step + run: exit 0 deployment-gate: name: Deployment Start Gate diff --git a/.github/workflows/ci-security.yml b/.github/workflows/ci-security.yml index 3ada9fae3f..2e6926d523 100644 --- a/.github/workflows/ci-security.yml +++ b/.github/workflows/ci-security.yml @@ -74,7 +74,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge + ref: refs/pull/${{ github.event.pull_request.number }}/head - name: Restore run: dotnet restore @@ -82,14 +82,22 @@ jobs: - name: Build ReleaseNotes run: dotnet publish -c Release -p:TGS_HOST_NO_WEBPANEL=true -o release_notes_bins tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj + - name: Checkout + uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + path: merge_workspace + - name: Read Current SHA # Can't rely on github.sha as it's for the base branch id: get-pr-sha - run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + run: | + cd merge_workspace + echo "merge_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - name: Generate Temporary Branch to Reference Merge + - name: Generate Temporary Branch to Reference Head run: | - git checkout -b ${{ github.event.pull_request.number }}-merge - git push -f -u origin ${{ github.event.pull_request.number }}-merge + git checkout -b ${{ github.event.pull_request.number }}-head + git push -f -u origin ${{ github.event.pull_request.number }}-head - name: Send Workflow Dispatch uses: lasith-kg/dispatch-workflow@5623bf13f09bbbbdb549ec692b070307f39b66ac #v2.0.0 + setup_node@v4 @@ -98,17 +106,15 @@ jobs: dispatch-method: workflow_dispatch owner: ${{ github.repository_owner }} repo: ${{ github.event.pull_request.base.repo.name }} - ref: ${{ github.event.pull_request.number }}-merge + ref: ${{ github.event.pull_request.number }}-head workflow: ci-pipeline.yml token: ${{ github.token }} workflow-inputs: | { "pull_request_number": "${{ github.event.pull_request.number }}" + "pull_request_merge_sha": "${{ steps.get-pr-sha.outputs.merge_sha }}" } - - name: Set CI Check Run (Pending) - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ steps.get-pr-sha.outputs.head_sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Pending ${{ steps.dispatch.outputs.run-id }} - - name: Delete Temporary Branch if: always() - run: git push -d origin ${{ github.event.pull_request.number }}-merge + run: git push -d origin ${{ github.event.pull_request.number }}-head diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index b1b83c4a49..46c887fd5f 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -59,7 +59,6 @@ static async Task Main(string[] args) var shaCheck = versionString.Equals("--winget-template-check", StringComparison.OrdinalIgnoreCase); var fullNotes = versionString.Equals("--generate-full-notes", StringComparison.OrdinalIgnoreCase); var nuget = versionString.Equals("--nuget", StringComparison.OrdinalIgnoreCase); - var ciCheck = versionString.Equals("--ci-check", StringComparison.OrdinalIgnoreCase); var genToken = versionString.Equals("--token-output-file", StringComparison.OrdinalIgnoreCase); if ((!Version.TryParse(versionString, out var version) || version.Revision != -1) @@ -68,7 +67,6 @@ static async Task Main(string[] args) && !shaCheck && !fullNotes && !nuget - && !ciCheck && !genToken) { Console.WriteLine("Invalid version: " + versionString); @@ -126,17 +124,6 @@ static async Task Main(string[] args) return await EnsureRelease(client); } - if (ciCheck) - { - if (args.Length < 5) - { - Console.WriteLine("Missing check parameters!"); - return 4543; - } - - return await CICheck(client, args[1], args[2], Enum.Parse(args[3]), Int64.Parse(args[4])); - } - if (genToken) { if (args.Length < 3) @@ -1675,72 +1662,6 @@ static async ValueTask GenerateAppCredentials(GitHubClient gitHubClient, string gitHubClient.Credentials = new Credentials(installToken.Token); } - enum CheckMode - { - Pending, - Started, - Cancelled, - Success, - Failure, - } - - static async ValueTask CICheck(GitHubClient gitHubClient, string ciTargetSha, string pemBase64, CheckMode mode, long runID) - { - await GenerateAppCredentials(gitHubClient, pemBase64, false); - - switch (mode) - { - case CheckMode.Pending: - await gitHubClient.Check.Run.Create(RepoOwner, RepoName, new NewCheckRun("CI Pipeline", ciTargetSha) - { - DetailsUrl = $"https://github.com/{RepoOwner}/{RepoName}/actions/runs/{runID}", - }); - break; - case CheckMode.Started: - var prChecks = await gitHubClient.Check.Run.GetAllForReference(RepoOwner, RepoName, ciTargetSha); - var theCheckWeWant = prChecks.CheckRuns.FirstOrDefault(x => x.App.Id == AppId); - if (theCheckWeWant != null) - { - await gitHubClient.Check.Run.Update(RepoOwner, RepoName, theCheckWeWant.Id, new CheckRunUpdate - { - Status = CheckStatus.InProgress, - StartedAt = DateTimeOffset.UtcNow, - }); - } - else - await gitHubClient.Check.Run.Create(RepoOwner, RepoName, new NewCheckRun("CI Pipeline", ciTargetSha) - { - DetailsUrl = $"https://github.com/{RepoOwner}/{RepoName}/actions/runs/{runID}", - Status = CheckStatus.InProgress, - StartedAt = DateTimeOffset.UtcNow, - }); - - break; - case CheckMode.Cancelled: - case CheckMode.Failure: - case CheckMode.Success: - var conclusion = mode switch - { - CheckMode.Cancelled => CheckConclusion.Cancelled, - CheckMode.Failure => CheckConclusion.Failure, - CheckMode.Success => CheckConclusion.Success, - _ => throw new InvalidOperationException("Impossible"), - }; - - var prChecks2 = await gitHubClient.Check.Run.GetAllForReference(RepoOwner, RepoName, ciTargetSha); - var theCheckWeWant2 = prChecks2.CheckRuns.First(x => x.App.Id == AppId); - await gitHubClient.Check.Run.Update(RepoOwner, RepoName, theCheckWeWant2.Id, new CheckRunUpdate - { - Status = CheckStatus.Completed, - CompletedAt = DateTimeOffset.UtcNow, - Conclusion = conclusion, - }); - break; - } - - return 0; - } - static void DebugAssert(bool condition, string message = null) { // This exists because one of the fucking asserts evaluates an enumerable or something and it was getting optimized out in release